我刚刚发明了以下Maybe
的替代定义:
type Maybe' a = forall b. (b -> (a -> b) -> b)
just :: a -> Maybe' a
just a = \d f -> f a
nothing :: Maybe' a
nothing = const
bind :: Maybe' a -> (a -> Maybe' b) -> Maybe' b
bind ma f = ma nothing (\a -> f a)
问题是我无法添加以下实例声明
instance Monad (Maybe') where
return = just
a >>= f = bind a f
错误消息是:
Type synonym Maybe' should have 1 argument, but has been given none
有什么办法可以解决吗?
答案 0 :(得分:8)
如果将其包装在Monad
中,则只能将其设为newtype
的实例。您还必须使用PolymorphicComponents
扩展名(RankNTypes
的较弱形式)来普遍量化b
:
{-# LANGUAGE PolymorphicComponents #-}
newtype Maybe' a = Maybe' { unMaybe' :: forall b. (b -> (a -> b) -> b) }
just :: a -> Maybe' a
just a = Maybe' (\d f -> f a)
nothing :: Maybe' a
nothing = Maybe' const
bind :: Maybe' a -> (a -> Maybe' b) -> Maybe' b
bind ma f = Maybe' (unMaybe' ma const (\a -> unMaybe' (f a)))
instance Monad Maybe' where
return = just
(>>=) = bind
你需要一个newtype的原因是Haskell类型的同义词不会“粘住”。当Haskell尝试将Maybe'
的类型签名与Monad
类型类的newtype匹配时,它根本看不到Maybe'
,而是看到原始底层函数类型。
Haskell使用“主要类型”来确保每种类型都具有普通形式。基础函数的正常形式是:
(->) b ((->) ((->) a b) b)
类型同义词不会更改类型的正常形式,但newtypes会更改。具体来说,在这种情况下,newtype
正在重新排列类型,以便普通表单现在具有a
作为Monad
实例所需的最后一个类型参数。
答案 1 :(得分:6)
类型同义词不是类型。使用newtype
,您将获得* -> *
类型,而使用类型同义词则不会。所以你的问题现在已经减少为什么类型同义词不是一流的。
答案可能是因为第一类同义词会产生太多歧义,并且在简单的情况下无法进行类型推断。
type First a b = (a, b)
type Second a b = (b, a)
type Both a = (a, a)
如果我们可以定义Functor (First a)
,Functor (Second a)
和Functor (Both a)
个实例,那么fmap (+1) (2, 3)
将是不明确的。
您的发明BTW称为Church encoding。教会可以编码任何东西。请参阅https://gist.github.com/rampion/2176199以了解Haskell中的少数教会编码(对,可能和列表)。
答案 2 :(得分:1)
无法部分应用类型同义词。
类型同义词只是帮助程序员的简短手段,而不是Haskell语言可以讨论的实际存在的东西。 Haskell处理类型同义词的唯一方法是“查看”它以查看右侧的类型。参数只是给你一种“宏语言”。
所以Maybe' a
完全等同于forall b. (b -> (a -> b) -> b)
。它的行为就像你写了forall b. (b -> (a -> b) -> b)
一样。但是Maybe'
本身没有争论呢? Haskell不能像Maybe'
那样处理它,它必须用其他东西替换它。但是什么?如果它意味着什么,那就必须是\a ~> (forall b. (b -> (a -> b) -> b)
,我使用\a ~> ...
作为类型级lambda的伪语法;从类型到另一种类型的任意函数。我必须为此编写语法的原因是因为Haskell没有这个语法,并且它没有语法的原因是因为它无法处理完全通用的类型级函数。
我不确定支持任意类型级别的lambdas(类型构造函数和类型族是否实际上是非常有限的形式)实际上是不可能的,但它肯定是非常困难的。因此不能部分应用类型同义词。
要获得可以创建Monad
实例的东西(需要可以应用于类型的东西来创建类型 - 某种类型* -> *
),你需要使用{{1或data
创建一个类型构造函数。类型同义词不能用于此目的。