在关注教程Functors, Applicatives, And Monads In Pictures及其JavaScript version。
后,我的问题出现了当文本说仿函数从上下文中展开值时,我理解Just 5
- > 5
转型正在发生。根据{{3}},Just在Maybe
monad的“范围”中定义。
我的问题是整个展开的东西是如此神奇?我的意思是,有一些语言规则会自动解开“范围”变量的问题是什么?在我看来,这个动作只是在某种表中查找,其中符号Just 5
对应于整数5
。
我的问题受到JavaScript版本的启发,其中Just 5
是原型数组实例。因此,展开确实根本不是火箭科学。
这是“计算”类型的理由还是“程序员”理由?为什么我们在编程语言级别上区分Just 5
和5
?
答案 0 :(得分:8)
首先,我不认为你可以理解Monads之类的东西而不理解类似Haskell的类型系统(即没有学习像Haskell这样的语言)。是的,有许多教程声称不然,但在学习Haskell之前我已经阅读了很多这些教程而且我没有得到它。所以我的建议是:如果你想了解Monads至少学习一些Haskell。
问题"为什么我们在编程语言层面上区分map
和Just 5
?"。为了类型安全。在大多数不是Haskell 5
,null
,nil
的语言中,通常用于表示缺少值。然而,这经常导致像whatever
这样的事情,因为你没有预料到价值可能不存在。
在Haskell中没有NullPointerExceptions
。因此,如果您的值为null
或其他任何值,则该值不能为Int
。你保证有价值。大!但有时你实际上想要/需要编码缺少值。在Haskell中,我们使用null
。因此Maybe
类型的某些内容可以是Maybe Int
或Just 5
。通过这种方式显然,值可能不存在,并且您不会意外忘记它可能是Nothing
,因为您必须显式地解包该值。
这与Monads没有任何关系,除了Nothing
碰巧实现了Monad类型类(如果你熟悉Java,类型类有点像Java接口)。那也许主要不是Monad,但恰好也是Monad。
答案 1 :(得分:5)
我认为你是从错误的方向看这个。 Monad
明确不关于展开。 Monad
是关于作文的。
它允许您将类型a -> m b
的函数与类型m a
的值组合(不一定适用)以获取类型m b
的值。我可以理解您可能认为明显的方法是将m a
类型的值展开为类型a
的值。但很少Monad
个实例以这种方式工作。实际上,唯一能够以这种方式工作的是与Identity
类型相同的那些。对于Monad
的几乎所有实例,只能打开一个值。
考虑Maybe
。如果起始值为Maybe a
,则无法将类型a
的值展开为类型Nothing
的值。 Monadic作品必须做一些比打包更有趣的事情。
考虑[]
。将类型[a]
的值展开为类型a
的值是不可能的,除非输入恰好是长度为1的列表。在所有其他情况下,monadic组合正在做一些比展开更有趣的事情。
考虑IO
。像getLine :: IO String
这样的值不包含String
值。打开它几乎是不可能的,因为它没有包装东西。 IO
值的monadic组合不会解开任何东西。它将IO
值组合成更复杂的IO
值。
我认为值得调整你对Monad
意味着什么的看法。如果它只是一个展开的界面,那将毫无用处。但它更加微妙。这是一个作文界面。
答案 2 :(得分:4)
一个可能的例子是:考虑Haskell类型Maybe (Maybe Int)
。其值可以是以下形式
Nothing
Just Nothing
Just (Just n)
表示某个整数n
如果没有Just
包装,我们就无法区分前两个。
实际上,可选类型Maybe a
的重点是向现有类型Nothing
添加新值(a
)。为确保此类Nothing
确实是一个新值,我们将其他值包装在Just
中。
在类型推断期间它也有帮助。当我们看到函数调用f 'a'
时,我们可以看到f
类型调用Char
,而不是类型Maybe Char
或Maybe (Maybe Char)
。类型类系统允许f
在每种情况下都有不同的实现(这类似于"重载"在某些OOP语言中)。
答案 3 :(得分:2)
什么"展开"意味着取决于容器。 Maybe
只是一个例子。 "解缠"当容器为[]
而不是Maybe
时,表示完全不同的东西。
关于整个展开的东西的神奇之处在于抽象:在Monad中我们有一个概念,即展开"展开"它抽象了容器的性质;然后它开始变得神奇的" ...
你问Just
意味着什么:只是通过数据声明定义的Haskell中的数据类型构造函数,如:
data Maybe a = Just a | Nothing
Just
获取a
类型的值,并创建类型为Maybe a
的值。 Haskell的方法是将a
类型的值与Maybe a
类型的值区分开来
答案 4 :(得分:2)
我的问题是,整个展开过程中有什么神奇之处?
没有什么神奇之处。您可以使用花园式模式匹配(此处以case
表达式的形式)来定义...
mapMaybe :: (a -> b) -> Maybe a -> Maybe b
mapMaybe f mx = case mx of
Just x -> Just (f x)
_ -> mx
...与fmap
的{{1}}完全相同。 Maybe
类唯一添加的东西 - 它是一个非常有用的东西,毫无疑问 - 是一个额外的抽象层次,它涵盖了可以映射的各种结构。
为什么我们在编程语言级别上区分
Functor
和Just 5
?
比5
和Just 5
之间的区别更有意义的是类型之间的区别 - 例如在5
和Maybe Int
之间。如果您有Int
,则可以确定x :: Int
是您可以使用的x
值。但是,如果您有Int
,则可能没有确定性,因为mx :: Maybe Int
可能会丢失(即Int
可能是mx
),类型系统会强制您承认并处理这种可能性。
另请参阅:jpath's answer,以便进一步评论Nothing
的有用性(不一定与Maybe
和Functor
等类相关联; Carl's answer有关Monad
和Functor
等类的有用性的进一步评论(Monad
示例之外)。
答案 5 :(得分:1)
首先,您需要从问题中删除monad。他们无事可做。把这篇文章视为monad的观点之一,也许它不适合你,在类型系统中你可能仍然很少理解,它会理解haskell中的monad。
因此,您的问题可以改为:为什么没有隐式转换Just 5 => 5
?但答案很简单。因为值Just 5
的类型为Maybe Integer
,所以此值可能为Nothing
,但在这种情况下必须执行什么编译?只有程序员才能解决这种情况。
但还有更令人不安的问题。有些类型,例如newtype Identity a = Identity a
。它只包含一些价值。那么,为什么没有隐性转换Identity a => a
?
简单的答案是 - 尝试实现这一点会导致不同的系统类型,这些系统类型不会具有当前存在的许多优良品质。根据这一点,它可以牺牲其他可能性。