【问题标题】:Satisfying monad laws without a type constructor在没有类型构造函数的情况下满足单子定律
【发布时间】:2012-08-17 21:20:21
【问题描述】:

给定例如像

这样的类型
data Tree a = Branch (Tree a) (Tree a)
            | Leaf a

我可以轻松地为 Functor、Applicative、Monad 等编写实例。

但如果“包含”类型是预先确定的,例如

data StringTree = Branch StringTree StringTree
                | Leaf String

我失去了编写这些实例的能力。

如果我要为我的 StringTree 类型编写函数

stringTreeReturn :: String -> StringTree
stringTreeBind   :: String -> (String -> StringTree) -> StringTree
stringTreeFail   :: String -> StringTree
-- etc.

满足单子定律,我还能说StringTree 是单子吗?

【问题讨论】:

  • fmap (const ()) 会导致什么结果?
  • 您甚至可以使用stringTreeFMap :: (String -> String) -> (StringTree -> StringTree),但要真正成为范畴论意义上的单子,您需要join :: m (m a) -> m a,这根本不适合。
  • 只是出于好奇,你为什么不能直接定义:type StringTree = Tree String
  • @GabrielGonzalez:我可以;这是一个纯粹的理论问题。
  • 您不能为StringTree 编写Monad 实例,但我相信您仍然可以通过使用RebindableSyntax 语言扩展来使用do 糖。

标签: haskell monads functor category-theory


【解决方案1】:

不,那不是单子。想想看,你能不能写一个 monad m 所以只有 m String 是可能的?

【讨论】:

    【解决方案2】:

    没有。您正在查看 typekind 之间的区别。简单地说,a kind 是 Haskell 对类型进行分类的方式,因此它是类型之上的抽象级别。 ghci 可以帮忙。

    :type stringReturn
    stringReturn :: String -> StringTree
    :type Functor
    <interactive>:1:1: Not in scope: data constructor `Functor'
    

    所以马上,我们可以看到一个函数,它有一个类型,与一个类型,它有一个种类是完全不同的。

    我们也可以向ghci询问种类。

    :kind Functor
    Functor (* -> *) -> Constraint
    :kind StringTree
    StringTree :: *
    :kind Tree
    Tree :: * -> *
    

    种类使用* 表示其符号中的变量。我们可以从上面的交互中看到Functor 需要一个在一个参数上参数化的类型。我们还可以看到StringTree不带任何参数,Tree带一个。

    这是表达和解开你的问题的一个漫长的过程,但希望它能够显示类型和种类之间的区别。

    【讨论】:

    • “A Gentle Introduction to Haskell, Version 98”中的“Type Classes and Overloading”部分简要介绍了kind,如果有帮助的话。
    【解决方案3】:

    Tree a 不是一个 monad,一般来说或对于任何特定的aTree 本身 是一个 monad。 monad 不是类型,它是 any 类型和该类型的“monadic 版本”之间的对应关系。例如,Integer 是整数类型,Maybe IntegerMaybe monad 中的整数类型。

    因此,StringTree 是一种类型,不能是 monad。它只是不一样的东西。您可以尝试将其想象为 monad 中的字符串类型,但您的函数 stringTreeReturn 等与它们的 monad 对应对象的类型不匹配。查看Maybe monad 中&gt;&gt;= 的类型:

    Maybe a -> (a -> Maybe b) -> Maybe b
    

    第二个参数是从aMaybe monad (Maybe b) 中的any 类型的函数。 stringTreeBind 的类型为:

    String -> (String -> StringTree) -> StringTree
    

    第二个参数只能是从StringString的一元版本的函数,而不是任何类型的一元版本。

    因此,对于StringTree 值,您不能对任意一元类型的值做所有可以做的事情,这就是为什么不能将其作为实例的原因。即使您可以以某种方式将其视为 monad,但当泛型 monadic 代码期望能够以对 StringTree 没有意义的方式使用泛型 monadic 操作时,事情就会开始出错。

    最终,如果你认为它“像”一个 monad,因为它在一个容器中的 String 的行为类似于 monadic 容器,那么最简单的做法就是让它成为任何类型的通用容器( Tree a)。如果您需要具有专门依赖于它作为字符串树的辅助功能,那么您可以将该代码编写为仅对 Tree String 值进行操作,并且它将很高兴地与通常在 Tree a 上工作的代码共存。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-12-21
      • 2017-04-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-02-03
      • 2020-01-29
      相关资源
      最近更新 更多