我仍然有时会失去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中做到吗?
答案 0 :(得分:3)
首先,Haskell中的type
不会声明新类型。 type
严格来说是类型别名,您至少要求的内容需要使用data
或newtype
获得的实际新类型。
现在,说出你想要的东西不能完全在haskell中完成 - 也就是说,你不能说"任何时候你看到Edge l
,那就是' s对l
(Label l)
"。
}限制
GADTs
。你这样做:
Edge
请注意,我必须为(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
方式调用它。我选择使用一个元组来制作一些代码,而不是这个答案的主要内容更短,但是作为一般Haskell风格的问题,你应该知道作为输入类型的元组是不受欢迎的。