使用特定的类定义类型同义词

雅各布丹尼尔

我有时仍然迷失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)”。

使用语言扩展功能,需要GADTsEdge是,在构造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)-如果需要的话,你需要手动进行实例为EqOrd太。

现在,我在上面说过的内容对构造函数提供了约束,但对类型没有约束:让我们想象一下,有一个这样添加的方法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] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章