繁体   English   中英

Haskell 中的部分应用类型

[英]Partially Applied Types in Haskell

基于这个问题,在这段代码中

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 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 Promise a的类型?

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

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

Int ::: *

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

Maybe ::: * -> *

它的功能类型与您在价值级别所期望的一样。


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

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

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

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

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


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

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

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

但是当然,正如你在这里指出的那样, 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有种类* (* -> *)不一致。

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

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

除此之外,你的代码很好。

我想添加到@J。 亚伯拉罕森的精彩回答。 我对 Haskell 类系统的很多理解都来自我强烈推荐的这个diogo castro 的博客

谈到部分应用类型的问题。 除了类型类之外,还可以在类型构造函数中使用它们。 以博客为例。

data NonEmpty f a = MkNonEmpty { head :: a, tail :: f a }
:k NonEmpty
-- NonEmpty :: (* -> *) -> * -> *
:t MkNonEmpty { head = (3 :: Int), tail = Maybe 3 }
-- :: NonEmpty Maybe Int

这是一个老问题,所以这可能是 Haskell 的最新补充。它可以概括为类型构造函数可以采用类型和类型构造函数作为 arguments。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM