Joh*_*ohn 3 types typeerror purescript
我在 Purescript 文件中定义了几种类型:
data Point = Point {
x :: Int,
y :: Int
}
data Rect = Rect {
upperLeft :: Point,
upperRight :: Point,
lowerLeft :: Point,
lowerRight :: Point
}
Run Code Online (Sandbox Code Playgroud)
现在我想定义一个函数来检查两个矩形是否重叠:
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)
Run Code Online (Sandbox Code Playgroud)
但是,这会导致以下错误:
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
Run Code Online (Sandbox Code Playgroud)
根据我对此消息的理解,编译器正在推断某种联合类型,其中xis t0 | t1,并且upperLeft也可以具有 type 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
Run Code Online (Sandbox Code Playgroud)
所以显然它推断出比我的Rect类型更通用的类型。但我只想要一个狭义定义的函数。如果有人能够阐明这里发生的事情,我将非常感激。
PureScript 中的记录与 Haskell 中的记录不同。
Haskell 没有像大多数其他语言所理解的真实记录。Haskell 的记录只是定义 ADT 字段访问器函数的一种方法。例如,以下两种类型几乎是等效的:
data A = A Int String
data B = B { x :: Int, y :: String }
Run Code Online (Sandbox Code Playgroud)
除了该类型B带有预定义的访问器函数x :: B -> Int和y :: B -> String.
另一方面,PureScript 拥有真实的、临时的、多态的、可扩展的记录,这些记录本身就是一个东西。PureScript 中的记录定义如下:
{ x :: Int, y :: String }
Run Code Online (Sandbox Code Playgroud)
没错,没有必要data,也没有什么。记录只是临时的,有点像 Haskell 中的元组。例如:
getX :: { x :: Int, y :: String } -> Int
getX r = r.x
Run Code Online (Sandbox Code Playgroud)
当然,如果您愿意,您可以为您的记录声明一个别名,就像您对任何其他类型所做的那样:
type XY = { x :: Int, y :: String }
getX :: XY -> Int
getX = r.x
Run Code Online (Sandbox Code Playgroud)
您还可以使用记录作为 ADT 的成员 - 再次,就像任何其他类型一样:
data XYWrapped = XYWrapped { x :: Int, y :: String }
Run Code Online (Sandbox Code Playgroud)
但如果这样做,则需要通过模式匹配解开 ADT 才能获取记录。同样,就像任何其他类型一样:
getXWrapped :: XYWrapped -> Int
getXWrapped (XYWrapped r) = r.x
Run Code Online (Sandbox Code Playgroud)
您也不限于仅包装一条记录(您猜对了:就像任何其他类型一样):
data XYPQ = XYPQ { x :: Int, y :: String } { p :: Char, q :: Int }
getXPlusQ :: XYPQ -> Int
getXPlusQ (XYPQ a b) = a.x + b.q
Run Code Online (Sandbox Code Playgroud)
有了这些知识,您现在可以看到您的类型Point实际上Rect不是记录,而是包装单个记录的 ADT(因此它们应该是newtypes)。
这就是您遇到类型不匹配的原因。因为您编写了r.upperLeft,所以编译器推断它r必须是包含名为 的字段的记录upperLeft,但您的类型签名表明它r的类型为Rect,这根本不是一条记录。所以编译器会抱怨。
要解决此问题,您可以将类型重新定义为实际记录:
type Point = {
x :: Int,
y :: Int
}
type Rect = {
upperLeft :: Point,
upperRight :: Point,
lowerLeft :: Point,
lowerRight :: Point
}
Run Code Online (Sandbox Code Playgroud)
或者您可以让您的函数解开 ADT:
rectsOverlap (Rect r) (Rect s) =
let (Point rUpperRight) = r.upperRight
(Point rUpperLeft) = r.upperLeft
....
Run Code Online (Sandbox Code Playgroud)
这会有点乏味。所以我会坚持第一个选择。
还要注意,如上所述,PureScript 记录可以是多态的,并且可以通过多态性进行扩展。有关这方面的更多信息,请参阅此答案。