Типы Purescript не объединяются - PullRequest
       24

Типы Purescript не объединяются

1 голос
/ 22 сентября 2019

Я определил пару типов в моем файле Purescript:

data Point = Point {
  x :: Int,
  y :: Int
}

data Rect = Rect {
  upperLeft :: Point,
  upperRight :: Point,
  lowerLeft :: Point,
  lowerRight :: Point
}

Теперь я хотел бы определить функцию, чтобы проверить, перекрываются ли два прямоугольника:

rectsOverlap :: Rect -> Rect -> Boolean
rectsOverlap r s = (r.upperLeft.x > s.lowerRight.x && r.upperLeft.y > s.lowerRight.y)
  || (r.upperLeft.x < s.lowerRight.x && r.upperLeft.y < s.lowerRight.y)
  || (r.lowerLeft.x > s.upperRight.x && r.lowerLeft.y > s.upperRight.y)
  || (r.lowerLeft.x < s.upperRight.x && r.lowerLeft.y < s.upperRight.y)

Однако,это приводит к следующей ошибке:

  Could not match type

    { upperLeft :: { x :: t0
                   | t1     
                   }        
    | t2                    
    }                       

  with type

    Rect


while checking that type Rect
  is at least as general as type { upperLeft :: { x :: t0
                                                | t1     
                                                }        
                                 | t2                    
                                 }                       
while checking that expression r
  has type { upperLeft :: { x :: t0
                          | t1     
                          }        
           | t2                    
           }                       
while checking type of property accessor r.upperLeft
while checking type of property accessor (r.upperLeft).x
in value declaration rectsOverlap

where t0 is an unknown type
      t1 is an unknown type
      t2 is an unknown type

Из того, что я понимаю из этого сообщения, компилятор выводит некоторый тип объединения, где x равен t0 | t1, а upperLeft также может иметь типt2.Когда я удаляю мою аннотацию типа, компилятор выводит следующий тип для этой функции:

rectsOverlap :: forall t45 t49 t52 t59 t72 t76 t81 t85 t87 t94.
  Ord t52 => Ord t59 => Ord t59 => Ord t87 => Ord t94 => Ord t87 => Ord t94 => { upperLeft :: { x :: t52
                                                                                              , y :: t59
                                                                                              | t45
                                                                                              }
                                                                               , lowerLeft :: { x :: t87
                                                                                              , y :: t94
                                                                                              | t81
                                                                                              }
                                                                               | t72
                                                                               }
                                                                               -> { lowerRight :: { x :: t52
                                                                                                  , y :: t59
                                                                                                  | t49
                                                                                                  }
                                                                                  , upperRight :: { x :: t87
                                                                                                  , y :: t94
                                                                                                  | t85
                                                                                                  }
                                                                                  | t76
                                                                                  }
                                                                                  -> Boolean

Так что, очевидно, он выводит более общий тип, чем мой Rect тип.Но я хочу только узко определенную функцию.Если бы кто-нибудь мог пролить свет на то, что здесь происходит, я был бы очень признателен.

1 Ответ

1 голос
/ 22 сентября 2019

Записи в PureScript не совпадают с записями в Haskell.

Haskell не имеет реальных записей, как их понимают в большинстве других языков.Записи Haskell - это просто способ определить функции доступа для полей ADT.Например, следующие два типа почти эквивалентны:

data A = A Int String
data B = B { x :: Int, y :: String }

За исключением того, что тип B поставляется с предопределенными функциями доступа x :: B -> Int и y :: B -> String.

PureScript, с другой стороныУ него были реальные, специальные, полиморфные, расширяемые записи, которые сами по себе.Запись в PureScript определяется следующим образом:

{ x :: Int, y :: String }

Правильно, нет необходимости в data или чем-либо еще.Записи просто случайные, вроде как кортежи в Haskell.Например:

getX :: { x :: Int, y :: String } -> Int
getX r = r.x

Конечно, если вы хотите, вы можете объявить псевдоним для своей записи, так же, как вы можете сделать это с любым другим типом:

type XY = { x :: Int, y :: String }

getX :: XY -> Int
getX = r.x

Вы также можетеиспользуйте запись в качестве члена ADT - опять же, как и любой другой тип:

data XYWrapped = XYWrapped { x :: Int, y :: String }

Но если вы сделаете это, вам понадобится развернуть ADT с помощью сопоставления с образцом, чтобы получить запись,Опять же, как и с любым другим типом:

getXWrapped :: XYWrapped -> Int
getXWrapped (XYWrapped r) = r.x

Вы не ограничены упаковкой только одной записи (как вы уже догадались: как и с любым другим типом):

data XYPQ = XYPQ { x :: Int, y :: String } { p :: Char, q :: Int }

getXPlusQ :: XYPQ -> Int
getXPlusQ (XYPQ a b) = a.x + b.q

Вооружившись этим знанием, теперь вы можете видеть, что ваши типы Point и Rect на самом деле не являются записями, а скорее ADT, которые переносят одну запись (и из-за этогоони должны быть newtype с).

Вот почему вы получаете несоответствие типов.Поскольку вы написали r.upperLeft, компилятор сделал вывод, что r должна быть записью, содержащей поле с именем upperLeft, но ваша подпись типа говорит, что r имеет тип Rect, который не является записью ввсе.Таким образом, компилятор жалуется.

Чтобы исправить это, вы можете либо переопределить ваши типы как фактические записи:

type Point = {
  x :: Int,
  y :: Int
}

type Rect = {
  upperLeft :: Point,
  upperRight :: Point,
  lowerLeft :: Point,
  lowerRight :: Point
}

, либо вы можете сделать так, чтобы ваша функция развернула ADT:

rectsOverlap (Rect r) (Rect s) = 
    let (Point rUpperRight) = r.upperRight
        (Point rUpperLeft) = r.upperLeft
    ....

что было бы довольно утомительно.Поэтому я остановлюсь на первом варианте.


Обратите также внимание, что, как упоминалось выше, записи PureScript могут быть полиморфными и расширяемыми посредством полиморфизма.Подробнее об этом см. этот ответ .

...