b ,当被视为“具有有趣的构造函数名称的类型”时,其类型为 (->) a b .这使它成为类型构造函数 (->)有两个参数-6ren">
gpt4 book ai didi

haskell - "Type constructed with type argument"的实例不需要用该类型的数据构造,在 Haskell 中

转载 作者:行者123 更新时间:2023-12-04 20:08:59 26 4
gpt4 key购买 nike

在 Haskell 中,类型构造函数当然可以接受类型参数。

一个函数a -> b ,当被视为“具有有趣的构造函数名称的类型”时,其类型为 (->) a b .这使它成为类型构造函数 (->)有两个参数,ab .这在“读者”模式中经常遇到,如 FunctorApplicative实例:

instance Functor ((->) a) where
fmap = (.)


instance Applicative ((->) a) where
pure = const
(<*>) f g x = f x (g x)

当我第一次尝试理解这个实例的用法时,如
fmap (+1) (*2) 3 (=== (+1) . (*2) $ 3 === 3*2+1 === 7)

我的 react 是“好吧, (+1) 的类型是 Int -> Int,也就是 (->) Int Int,所以匹配 Functor .... 但是 Int 在哪里?我通过调用 Maybe Int 来调用 Just 1 67 ,但我从来没有通过将任何东西应用于 (->) Int Int 来制作 Int 。事实上,我通过将它应用于 ((->) Int Int) 来破坏 Int !(是的,似乎有 Nothing ,但是……堕落了。)”

这一切都有效(当然),只要我记得,仅仅因为类型是从构造函数+参数构建的,并不意味着它的值是从相应类型的构造函数+参数构建的。一些最有趣、最强大(也很难理解)的类型构造函数是这样的( (->)LensArrow 等)

(好吧,真的是 Num a => a ,而不是 Int ,但让我们忽略它,不相关)

这个概念有名字吗? 在不依赖于误导性和无能的拐杖解释的情况下,思考类型构造函数的适当思维模型是什么“ Foo a 是一个结构 Foo 包含类型 a 的值)?

最佳答案

这个概念被称为逆变仿函数,在 Haskell 中是一个 Contravariant类型。

class Contravariant f where
contramap :: (b -> a) -> f a -> f b

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

更一般地,我们可以将类型中的类型变量视为具有逆变或协变性质(最简单)。例如,默认情况下我们有
newtype Reader t a = Reader (t -> a)

instance Functor (Reader t) where
fmap ab (Reader ta) = Reader (ab . ta)

其中表示第二个类型参数为 Reader是协变的,而如果我们颠倒顺序
newtype RevReader a t = RevReader (t -> a)

instance Contravariant (RevReader a) where
contramap st (RevReader ta) = RevReader (ta . st)
Contravariant 的有用直觉类型是它们有能力消耗零、一个或多个逆变参数值,而不是像我们在考虑 Functor 时经常想到的那样包含零、一个或多个协变参数值。 s。

结合这两个概念是 Profunctor
class Profunctor p where
dimap :: (a -> b) -> (c -> d) -> p b c -> p a d

正如我们注意到的,它要求 p很亲切 * -> * -> *其中第一个类型参数是逆变的,第二个是协变的。此类很好地表征了 (->)类型构造器
instance Profuntor (->) where
dimap f g h = g . h . f

同样,如果我们认为逆变类型参数被消费而协变类型参数被产生,这完全符合 (->) 的典型直觉。类型。

逆变参数类型的更多示例包括 Relation
newtype Relation t = Relation (t -> t -> Bool)

instance Contravariant Relation where
contramap g (Relation pred) = Relation $ \a b -> pred (g a) (g b)

Fold将左折叠表示为数据类型
newtype Fold a b = Fold b (a -> Fold a b)

instance Profunctor Fold where
dimap f g (Fold b go) = Fold (g b) (go . f)

sumF :: Num a => Fold a a
sumF = go 0 where
go n = Fold n (\i -> go (n + i))

Fold a b我们看到它消耗了任意数量的 a类型生产一个 b类型。

通常我们发现,虽然通常情况下我们有协变和“容器”(严格为正)类型,其中某种类型的值 c aa -> c a 类型的构造函数产生和一些填充值 a ,一般来说是不成立的。特别是我们有这样的协变类型,但也有逆变类型,它们通常是以某种方式消耗其参数化类型变量值的过程,或者甚至更奇特的类型,如完全忽略其类型变量的幻像类型
newtype Proxy a = Proxy -- need no `a`, produce no `a`

-- we have both this instance
instance Functor Proxy where
fmap _ Proxy = Proxy

-- and this one, though both instances ignore the passed function
instance Contravariant Proxy where
contramap _ Proxy = Proxy

和...“没什么特别的”类型变量不能具有任何性质,通常是因为它们被用作协变和逆变类型。
data Endo a = Endo (a -> a)

-- no instance Functor Endo or Contravariant Endo, it needs to treat
-- the input `a` differently from the output `a` such as in
--
-- instance Profunctor (->) where

最后,接受多个参数的类型构造函数对于每个参数可能具有不同的性质。但是,在 Haskell 中,最终类型参数通常被特殊对待。

关于haskell - "Type constructed with type argument"的实例不需要用该类型的数据构造,在 Haskell 中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20942079/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com