Haskell中的部分应用类型

时间:2014-05-27 21:15:18

标签: haskell

基于this问题,在此代码中

data Promise a b =  Pending (a -> b) | Resolved b | Broken

instance Functor (Promise x) where
    fmap f (Pending g) = Pending (f . g)

如果

g :: a -> b

,然后

Pending g :: Promise a b

同时

f :: b -> c

因为存在f . g

这意味着

Pending (f . g) :: Promise a c`.

结束

fmap :: (b -> c) -> Promise a b -> Promise a c

现在只有fmap有这个签名(适用于上述内容)

fmap :: Functor f => (b -> c) -> f b -> f c

如果你假设f = Promise a,这只会符合。虽然最终产品看似合理,但您如何解释f的类型或等效部分应用的承诺Promise a的类型?

2 个答案:

答案 0 :(得分:8)

在类型级别,您有另一种编程语言,几乎 -Haskell。特别是,您可以将类型视为具有构造函数并且能够部分应用。

为了更严格地看待这一点,我们引入了名为“种类”的“类型类型”。例如,类型构造函数Int具有种类

Int ::: *

我写(:::)来读“有点”,尽管这不是有效的Haskell语法。现在我们也有“部分应用类型构造函数”,如

Maybe ::: * -> *

它的函数类型就像你期望的那样。


对于种类的概念,有一个非常重要的概念 - 值只有在类型为*时才可以实例化类型。或者,例如,不存在类型Maybe

的值
x :: Maybe
x = -- .... what!

事实上,除了*之外,我们甚至不能在任何地方表达那种我们期望该类型描述价值的类型。

这导致Haskell中“类型级函数”的力量受到某种限制,因为它们不能普遍地传递“未应用的类型构造函数”,因为它们并不总是有意义。相反,整个系统的设计使得只能构造合理的类型。

但允许表达这些“更高级别的类型”的地方是类型定义。


如果我们启用KindSignatures,那么我们可以直接编写我们的类型。这显示的一个地方是类定义。这是Show

class Show (a :: *) where
  show :: a -> String
  ...

这是完全自然的,因为a方法的签名中Show类型的出现具有价值。

但是,正如您在此处所指出的那样,Functor是不同的。如果我们写出它的亲切签名,我们就会明白为什么

class Functor (f :: * -> *) where
  fmap :: (a -> b) -> f a -> f b

这是一种非常新颖的多态性,更高级的多态性,因此需要花一点时间才能完全掌控它。但值得注意的是,f仅出现在Functor 的方法中应用于其他类型ab。特别是,像这样的类将被拒绝

class Nope (f :: * -> *) where
  nope :: f -> String

因为我们告诉系统f(* -> *)种,但我们使用它就像它可以实例化值一样,好像它是*一样。


通常,我们不必使用KindSignatures,因为Haskell可以直接推断签名。例如,我们可以(实际上)写

class Functor f where
  fmap :: (a -> b) -> f a -> f b

和Haskell推断f的类型必须为(* -> *),因为它似乎已应用于ab。同样地,如果我们写出一些不一致的东西,我们就会失败“类型检查”。例如

class NopeNope f where
  fmap :: f -> f a -> a

表示f类型* (* -> *)不一致。

答案 1 :(得分:0)

您只缺少ResolvedBroken的等式。我能想到的唯一合理的实现是

fmap f (Resolved x) = Resolved (f x)
fmap _ Broken = Broken

除此之外,您的代码还可以。