Haskell类型约束

时间:2016-01-01 17:38:08

标签: haskell typeclass

我正在努力使用haskell类型系统。基本上我要做的是定义一个代表monad的数据结构(参见代码示例中的 Ast 数据类型)(它可能是其他东西)。此类型由 a 参数化,并且没有约束。我的问题是当我想要反省这种类型时,我可能需要对类型参数进行一些约束,例如:

{-# LANGUAGE GADTs #-}

import Control.Monad

data Ast a where
    Bind :: Ast a -> (a -> Ast b) -> Ast b
    Return :: a -> Ast a

instance Functor Ast where
    fmap = liftM

instance Applicative Ast where
    pure = Return
    (<*>) = ap

instance Monad Ast where
    (>>=) = Bind

instance Show a => Show (Ast a) where
    show (Bind x y) = "bind " ++ {- begin error -} show x {- end error -} 
    show (Return y) = "return " ++ show y

这会产生以下错误:

Could not deduce (Show a1) arising from a use of ‘show’
from the context (Show a)
  bound by the instance declaration at src/Main.hs:21:10-31
Possible fix:
  add (Show a1) to the context of the data constructor ‘Bind’
In the second argument of ‘(++)’, namely ‘show x’
In the expression: "bind " ++ show x
In an equation for ‘show’: show (Bind x y) = "bind " ++ show x

这是有道理的,编译器不知道 x Show 的实例。我的问题是我可以表达这种约束吗?理想情况下,我只想在我的 Show 实例上设置约束,但我也尝试在 Ast 构造函数中添加 Show 约束:< / p>

data Ast a where
    Bind :: (Show a, Show b) => Ast a -> (a -> Ast b) -> Ast b
    Return :: a -> Ast a

我收到了这个错误:

No instance for (Show a) arising from a use of ‘Bind’
Possible fix:
  add (Show a) to the context of
    the type signature for (>>=) :: Ast a -> (a -> Ast b) -> Ast b
In the expression: Bind
In an equation for ‘>>=’: (>>=) = Bind
In the instance declaration for ‘Monad Ast’

我理解应该在 monad 类类型定义级别添加 Show 约束。

知道如何管理吗?

感谢。

1 个答案:

答案 0 :(得分:1)

你无法双管齐下。

Functor类,其中Monad是一个子类,坚持完全多态(fmap可以应用于具有任意返回类型的函数)。所以你不能限制这方面的事情。您也无法在Bind内限制它们,因为>>=必须接受任意左操作数。

另一种方法是使用仿函数和monad的不同概念(查找“索引monad”)。以下代码完全未经测试。

{-# Language DataKinds, Gadts, KindSignatures #-}

-- (type-level) free magma
data FM a = Leaf | Bin (FM a) a (FM a)

data Ast :: FM * -> * -> * where
  Return :: a -> Ast 'Leaf a
  Bind :: Ast ts a -> (a -> Ast us b) -> Ast ('Bin ts a us) b

class Showy (mag :: FM *) where
  showy :: Show a => Ast mag a -> String

instance Showy 'Nil where
  showy (Return a) = "Return " ++ show a

instance (Showy as, Show b) => Showy ('Bin as b cs) where
  showy (Bind m f) = "Bind " ++ showy m

instance (Showy as, Show b) => Show (Ast as b) where
  show = showy