我有时仍然迷失Haskell的语法,但是这次我不确定是否可以做我想做的事情。
我想拥有的是
import System.IO
class (Eq l, Show l) => Label l -- assume there might be some `where ...`
type Edge l = Label l => (Int, l, Int) -- my attempt on restricting l to be Label
type Graph l = [Edge l] -- inherently requiring l to be Label
addEdge :: Graph l -> Edge l -> Graph l
addEdge g e = e : g
data MyLabel = A | B | C deriving (Eq, Show)
instance Label MyLabel
main :: IO ()
main = do
putStrLn $ show $ addEdge [] (1, A, 2) -- producing Graph MyLabel
putStrLn $ show $ addEdge [] (1, 3, 2) -- not passing type checks
我所能做的就是将Label l =>
上下文添加到所有类似的函数中,addEdge
但是仍然允许Graph
带有标签的实例不符合该类Label
,并且还会复制约束。另一方面,在类型上具有约束(以某种方式)将节省大量工作(没有适当的标签类型,永远不可能只是Edge
/的任何实例Graph
)。
我可以在Haskell中以某种方式做到吗?
因此首先,type
在Haskell中不声明新类型。type
严格来说,它是类型别名,而您要提供的最低要求是您使用data
或所获得的那种实际的新类型newtype
。
现在,这就是说,你想要什么不能完全在Haskell做-那就是,你不能说“你看任何时候Edge l
,有如下隐含的约束l
,上面写着(Label l)
”。
使用语言扩展功能,您需要做GADTs
的Edge
是,在构造an的任何时候,构造an的类型都必须符合limit (Label l)
。您可以这样操作:
{-# LANGUAGE GADTs #-}
class (Eq l, Show l) => Label l -- assume there might be some `where ...`
data Edge l where
Edge :: (Label l) => (Int, l, Int) -> Edge l
type Graph l = [Edge l] -- does not implicitly provide a (Label l)
-- constraint on the type, but any actual object
-- will necessarily meet that type
instance Show (Edge l) where
show (Edge a) = "Edge " ++ show a
addEdge :: Graph l -> Edge l -> Graph l
addEdge g e = e : g
data MyLabel = A | B | C deriving (Eq, Show)
instance Label MyLabel
main :: IO ()
main = do
print $ addEdge [] $ Edge (1, A, 2) -- producing Graph MyLabel
-- print $ addEdge [] $ Edge (1, 3, 2) -- not passing type checks
请注意,我不得不添加一个实例Show (Edge l)
-如果需要的话,你需要手动进行实例为Eq
和Ord
太。
现在,我在上面说过的内容对构造函数提供了约束,但对类型没有约束:让我们想象一下,有一个这样添加的方法Label l
:
class (Eq l, Show l) => Label l where
defLabel :: l
用实例声明Label MyLabel
修改为说:
instance Label MyLabel where
defLabel = A
然后,如果您尝试编写此方法,它将无法正常工作:
newGraph :: Int -> Graph l -- won't work with this type
newGraph n = zipWith (\f t -> Edge (f, defLabel, t)) [1..n-1] [2..]
相反,您需要将(Label l)
约束添加为:
newGraph :: (Label l) => Int -> Graph l -- works with this type
newGraph n = zipWith (\f t -> Edge (f, defLabel, t)) [1..n-1] [2..]
但是,如果只处理已经构造Edge
的,则可以依赖于构造(Label l)
时使用的隐式用法Edge
。例如,这将编译:
isAllDefault :: Graph l -> Bool -- Look, no Label constraint
isAllDefault [] = True
isAllDefault (Edge (_, l, _):gs) = l == defLabel && isAllDefault gs
-- but I can still use defLabel here
-- and can rely on (Eq l) from the Label definition
样式说明:大多数人将Edge
构造函数编写为:
Edge :: (Label l) => Int -> l -> Int -> Edge l
这样您就可以将其调用为Edge 2 A 1
。我选择使用元组在上面编写一些代码,这并不是该答案的要点,但是作为一般的Haskell风格,您应该知道元组作为输入类型是不被接受的。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句