haskell - 理解 Data.Functor.Constant 构造函数和应用规律

标签 haskell constants functor applicative monoids

我对 Data.Functor.Constant 的类型构造函数以及它如何与应用程序一起使用感到困惑。

首先是构造函数:

当我检查 Constant :: a -> Constant a b 的类型时

我看到它需要 a , 但返回 Constant a bb 在哪里来自,为什么存在?

其次,我在 Applicative 上苦苦挣扎:

我了解 Constant 需要在内部有一个 Monoid 才能成为 Applicative 实例。

它必须遵守的一条法律是:pure id <*> Constant x = x
我认为这与:Constant id <*> Constant x = x 相同

但我想我错了,因为下面的代码清楚地显示了纯粹的不同行为。

:t pure id <*> Constant "hello" // Constant [Char] b

:t Constant id <*> Constant "hello" // Couldn't match expected type `a0 -> a0' with actual type `[Char]'

:t pure id <*> Constant reverse //  Constant ([a] -> [a]) b

:t Constant id <*> Constant reverse // Constant ([a] -> [a]) b

我看到它只有在 x 时才有效是同一个幺半群,除非我使用纯的。所以我不确定为什么 pure 的工作方式不同。我怀疑这与 b 有关这就是为什么他们在同一个问题上。

总结一下这两个问题:
  • b 是什么意思在常量构造函数中做什么?
  • 为什么即使幺半群内部不同,pure 也会起作用?

  • 非常感谢!

    最佳答案

    好的,所以你有这种类型

    data Const a b = Const { getConst :: a }
    

    您的第一个问题是“b 来自哪里?”

    答案是它不是来自任何地方。就像你可以想到 Maybe b作为容纳 0 或 1 个 b 类型值的容器, 一个 Const a b是一个容器,它恰好包含 b 类型的 0 个值(但确实拥有 a 类型的值)。

    你的第二个问题是“为什么会在那里?”

    好吧,有时让一个仿函数说它可能包含 b 类型的值很有用。 ,但实际上包含其他东西(例如,想想 Either a b 仿函数——不同之处在于 Either a b 可能包含 b 类型的值,而 Const a b 绝对没有)。

    然后您询问了代码片段 pure id <*> Const "hello"Const id <*> Const "hello" .你以为这些是一样的,但事实并非如此。原因是 Applicative Const 的实例好像
    instance Monoid m => Applicative (Const m) where
      -- pure :: a -> Const m a
      pure _ = Const mempty
    
      -- <*> :: Const m (a -> b) -> Const m a -> Const m b
      Const m1 <*> Const m2 = Const (m1 <> m2)
    

    由于实际上没有任何值具有第二个参数的类型,我们只需要处理那些具有第一个参数类型的值,我们知道它是一个幺半群。这就是为什么我们可以制作 Const Applicative 的一个实例-- 我们需要提取 m 类型的值从某个地方,和Monoid instance 为我们提供了一种从无到有的方法(使用 mempty )。

    那么在你的例子中发生了什么?你有 pure id <*> Const "hello"必须有类型 Const String a自从 id :: a -> a .在这种情况下,幺半群是 String .我们有mempty = ""对于 String , 和 (<>) = (++) .所以你最终得到
    pure id <*> Const "hello" = Const "" <*> Const "hello"
                              = Const ("" <> "hello")
                              = Const ("" ++ "hello")
                              = Const "hello"
    

    另一方面,当你写 Const id <*> Const "hello"左侧参数的类型为 Const (a -> a) b右边的类型为 Const String b并且您看到类型不匹配,这就是您收到类型错误的原因。

    现在,为什么这很有用?一个应用程序位于 lens库,它允许您在纯函数设置中使用 getter 和 setter(熟悉命令式编程)。镜头的简单定义是
    type Lens b a = forall f. Functor f => (a -> f a) -> (b -> f b)
    

    即,如果你给它一个函数来转换 a 类型的值, 它会给你一个函数来转换 b 类型的值.那有什么用?好吧,让我们选择 a -> f a 类型的随机函数对于特定的仿函数f .如果我们选择 Identity仿函数,看起来像
    data Identity a = Identity { getIdentity :: a }
    

    那么如果 l是镜头,定义
    modify :: Lens b a -> (a -> a) -> (b -> b)
    modify l f = runIdentity . l (Identity . f)
    

    为您提供一种方法来获取转换 a 的函数s 并将它们转换为转换 b 的函数s。
    a -> f a 类型的另一个函数我们可以传入的是Const :: a -> Const a a (请注意,我们已经专门化,以便第二种类型与第一种相同)。然后镜头的 Action l就是把它变成b -> Const a b类型的函数,这告诉我们它可能包含 b , 但实际上它实际上包含一个 a !一旦我们将它应用于 b 类型的东西为了得到 Const a b , 我们可以用 getConst :: Const a b -> a提取 a 类型的值从帽子里出来。因此,这为我们提供了一种提取 a 类型值的方法。来自 b - 即它是一个 setter/getter 。定义看起来像
    get :: Lens b a -> b -> a
    get l = getConst . l Const
    

    作为一个镜头的例子,你可以定义
    first :: Lens (a,b) a
    first f (a,b) = fmap (\x -> (x,b)) (f a)
    

    这样你就可以打开一个 GHCI session 并编写
    >> get first (1,2)
    1
    >> modify first (*2) (3,4)
    (6,4)
    

    正如您可能想象的那样,它在各种情况下都很有用。

    关于haskell - 理解 Data.Functor.Constant 构造函数和应用规律,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21169943/

    相关文章:

    c++ - 指针类型的 STL 函数对象

    c++ - const int 和 const double 在编译时的区别

    c++ - GCC7 在同一个类中推断出存在 const 修饰符

    haskell - 为代数数据类型定义 TH Lift 实例

    haskell - fmap (f . g) = fmap f . 中模式 : f . g 中的解析错误函数映射g

    c++ - const auto * 和 const auto 之间的区别?

    clojure - 映射 HashMap 中的每个值

    c++ - 为什么从 C++11 中删除了 unary_function、binary_function?

    haskell - 如何实现 mapAccumM?

    haskell - 检查特定的数据构造函数