单子 "unboxing"
Monad "unboxing"
我的问题是在学习教程 Functors, Applicatives, And Monads In Pictures and its JavaScript version 时提出的。
当文字说仿函数从上下文中解包值时,我理解 Just 5
-> 5
转换正在发生。根据 What does the "Just" syntax mean in Haskell? ,只是 Maybe
monad 的 "defined in scope"。
我的问题是整个展开的东西有什么神奇之处?我的意思是,有一些自动解包 "scoped" 变量的语言规则有什么问题?在我看来,此操作只是某种 table 中的查找,其中符号 Just 5
对应于整数 5
.
我的问题受到JavaScript版本的启发,其中Just 5
是原型数组实例。所以展开确实不是火箭科学。
这是 "for-computation" 类型的原因还是 "for-programmer" 类型的原因?为什么我们在编程语言层面上区分Just 5
和5
?
"unwrap" 的含义取决于容器。 Maybe
只是一个例子。当容器是 []
而不是 Maybe
.
时,"Unwrapping" 意味着完全不同的东西
整个解包的神奇之处在于抽象:在 Monad 中,我们有一个 "unwrapping" 的概念,它抽象了容器的性质;然后它开始变得 "magical"...
你问 Just
是什么意思:Just 只不过是 Haskell 中的数据类型构造函数,通过数据声明定义,如 :
data Maybe a = Just a | Nothing
Just
获取类型 a
的值并创建类型 Maybe a
的值。这是 Haskell 区分类型 a
的值和类型 Maybe a
的方法
首先,我认为如果不了解类似 Haskell 的类型系统(即不学习像 Haskell 这样的语言),您就无法理解 Monad 等。是的,有很多教程声称不是这样,但是我在学习之前已经阅读了很多教程 Haskell 并且我没有理解。所以我的建议是:如果你想了解 Monad,至少要学习一些 Haskell.
关于你的问题"Why do we distinguish Just 5
from 5
on the programming language level?"。为了类型安全。在大多数恰好不是 Haskell null
、nil
、whatever
的语言中,通常用来表示没有值。然而,这通常会导致 NullPointerExceptions
之类的结果,因为您没有预料到可能不存在值。
在Haskell中没有null
。因此,如果您有一个 Int
类型的值,或者其他任何类型的值,则该值不能是 null
。你保证有一个值。伟大的!但有时你实际上 want/need 来编码值的缺失。在 Haskell 中,我们为此使用 Maybe
。所以 Maybe Int
类型的东西可以是 Just 5
或 Nothing
之类的东西。这样很明显,该值可能不存在,并且您不会不小心忘记它可能是 Nothing
因为您必须显式解包该值。
这与 Monad 没有任何关系,除了 Maybe
恰好实现了 Monad 类型 class(类型 class 有点像 Java界面,如果您熟悉 Java)。那就是 Maybe 主要不是 Monad,但恰好也是 Monad。
一个可能的例子是:考虑 Haskell 类型 Maybe (Maybe Int)
。它的值可以是下面的形式
Nothing
Just Nothing
Just (Just n)
对于某个整数 n
没有 Just
包装器,我们无法区分前两个。
的确,可选类型 Maybe a
的全部意义在于向现有类型 a
添加一个新值 (Nothing
)。为了确保 Nothing
确实是一个新值,我们将其他值包装在 Just
.
中
它在类型推断期间也有帮助。当我们看到函数调用 f 'a'
时,我们可以看到 f
是在类型 Char
上调用的,而不是在类型 Maybe Char
或 Maybe (Maybe Char)
上调用的。类型类系统将允许 f
在每种情况下都有不同的实现(这类似于某些 OOP 语言中的 "overloading")。
我认为你从错误的方向来看这个问题。 Monad
明确地 不是 关于展开。 Monad
关于构图。
它允许您将类型 a -> m b
的函数与类型 m a
的值组合(不一定应用)以获得类型 m b
的值。我能理解您可能认为这样做的明显方法是将 m a
类型的值解包为 a
类型的值。但是很少有 Monad
个实例是这样工作的。事实上,唯一可以那样工作的是那些等同于 Identity
类型的。对于 Monad
的几乎所有实例,都无法展开值。
考虑 Maybe
。当起始值为 Nothing
时,将 Maybe a
类型的值展开为 a
类型的值是不可能的。 Monadic 组合必须做一些比展开更有趣的事情。
考虑 []
。将 [a]
类型的值展开为 a
类型的值是不可能的,除非输入恰好是长度为 1 的列表。在所有其他情况下,单子组合所做的事情比展开更有趣。
考虑 IO
。 getLine :: IO String
之类的值不包含 String
值。显然不可能打开包装,因为它没有包装任何东西。 IO
值的单子组合不会解包任何东西。它将 IO
个值组合成更复杂的 IO
个值。
我认为值得调整您对 Monad
含义的看法。如果它只是一个解包接口,那将毫无用处。不过,它更微妙。这是一个组合界面。
首先,您需要从问题中删除 monad。他们与此无关。将这篇文章作为对 monad 的一种观点,也许它不适合你,你可能在类型系统中仍然一知半解 haskell.
因此,您的问题可以改写为:为什么没有隐式转换 Just 5 => 5
?但答案很简单。因为值 Just 5
的类型是 Maybe Integer
,所以这个值可能是 Nothing
,但在这种情况下编译器必须做什么?只有程序员才能解决这种情况。
但是还有一个更不舒服的问题。有类型,例如 newtype Identity a = Identity a
。它只是一些价值的包装。那么,为什么没有隐式转换Identity a => a
?
简单的答案是 - 试图实现这一点会导致不同的系统类型,它不会有当前存在的许多优良品质。据此,可以为了其他可能性的利益而牺牲它。
My question is, what is so magical about the whole unwrapping thing?
这没什么神奇的。您可以使用普通的模式匹配(此处为 case
表达式的形式)来定义...
mapMaybe :: (a -> b) -> Maybe a -> Maybe b
mapMaybe f mx = case mx of
Just x -> Just (f x)
_ -> mx
... 这与 Maybe
的 fmap
完全相同。 Functor
class 添加的唯一东西——毫无疑问,这是一个非常有用的东西——是一个额外的抽象级别,涵盖了可以映射的各种结构。
Why do we distinguish Just 5
from 5
on programming language level?
比 Just 5
和 5
之间的区别更有意义的是它们 类型 之间的区别——例如在 Maybe Int
和 Int
之间。如果您有 x :: Int
,您可以确定 x
是一个您可以使用的 Int
值。但是,如果你有 mx :: Maybe Int
,你就没有这样的确定性,因为 Int
可能会丢失(即 mx
可能是 Nothing
),并且类型系统会强制你承认并处理这种可能性。
另请参阅: for further comments on the usefulness of Maybe
(which isn't necessarily tied to classes such as Functor
and Monad
); ,了解有关 class 的有用性的更多评论,例如 Functor
和 Monad
(除了 Maybe
示例)。
我的问题是在学习教程 Functors, Applicatives, And Monads In Pictures and its JavaScript version 时提出的。
当文字说仿函数从上下文中解包值时,我理解 Just 5
-> 5
转换正在发生。根据 What does the "Just" syntax mean in Haskell? ,只是 Maybe
monad 的 "defined in scope"。
我的问题是整个展开的东西有什么神奇之处?我的意思是,有一些自动解包 "scoped" 变量的语言规则有什么问题?在我看来,此操作只是某种 table 中的查找,其中符号 Just 5
对应于整数 5
.
我的问题受到JavaScript版本的启发,其中Just 5
是原型数组实例。所以展开确实不是火箭科学。
这是 "for-computation" 类型的原因还是 "for-programmer" 类型的原因?为什么我们在编程语言层面上区分Just 5
和5
?
"unwrap" 的含义取决于容器。 Maybe
只是一个例子。当容器是 []
而不是 Maybe
.
整个解包的神奇之处在于抽象:在 Monad 中,我们有一个 "unwrapping" 的概念,它抽象了容器的性质;然后它开始变得 "magical"...
你问 Just
是什么意思:Just 只不过是 Haskell 中的数据类型构造函数,通过数据声明定义,如 :
data Maybe a = Just a | Nothing
Just
获取类型 a
的值并创建类型 Maybe a
的值。这是 Haskell 区分类型 a
的值和类型 Maybe a
首先,我认为如果不了解类似 Haskell 的类型系统(即不学习像 Haskell 这样的语言),您就无法理解 Monad 等。是的,有很多教程声称不是这样,但是我在学习之前已经阅读了很多教程 Haskell 并且我没有理解。所以我的建议是:如果你想了解 Monad,至少要学习一些 Haskell.
关于你的问题"Why do we distinguish Just 5
from 5
on the programming language level?"。为了类型安全。在大多数恰好不是 Haskell null
、nil
、whatever
的语言中,通常用来表示没有值。然而,这通常会导致 NullPointerExceptions
之类的结果,因为您没有预料到可能不存在值。
在Haskell中没有null
。因此,如果您有一个 Int
类型的值,或者其他任何类型的值,则该值不能是 null
。你保证有一个值。伟大的!但有时你实际上 want/need 来编码值的缺失。在 Haskell 中,我们为此使用 Maybe
。所以 Maybe Int
类型的东西可以是 Just 5
或 Nothing
之类的东西。这样很明显,该值可能不存在,并且您不会不小心忘记它可能是 Nothing
因为您必须显式解包该值。
这与 Monad 没有任何关系,除了 Maybe
恰好实现了 Monad 类型 class(类型 class 有点像 Java界面,如果您熟悉 Java)。那就是 Maybe 主要不是 Monad,但恰好也是 Monad。
一个可能的例子是:考虑 Haskell 类型 Maybe (Maybe Int)
。它的值可以是下面的形式
Nothing
Just Nothing
Just (Just n)
对于某个整数n
没有 Just
包装器,我们无法区分前两个。
的确,可选类型 Maybe a
的全部意义在于向现有类型 a
添加一个新值 (Nothing
)。为了确保 Nothing
确实是一个新值,我们将其他值包装在 Just
.
它在类型推断期间也有帮助。当我们看到函数调用 f 'a'
时,我们可以看到 f
是在类型 Char
上调用的,而不是在类型 Maybe Char
或 Maybe (Maybe Char)
上调用的。类型类系统将允许 f
在每种情况下都有不同的实现(这类似于某些 OOP 语言中的 "overloading")。
我认为你从错误的方向来看这个问题。 Monad
明确地 不是 关于展开。 Monad
关于构图。
它允许您将类型 a -> m b
的函数与类型 m a
的值组合(不一定应用)以获得类型 m b
的值。我能理解您可能认为这样做的明显方法是将 m a
类型的值解包为 a
类型的值。但是很少有 Monad
个实例是这样工作的。事实上,唯一可以那样工作的是那些等同于 Identity
类型的。对于 Monad
的几乎所有实例,都无法展开值。
考虑 Maybe
。当起始值为 Nothing
时,将 Maybe a
类型的值展开为 a
类型的值是不可能的。 Monadic 组合必须做一些比展开更有趣的事情。
考虑 []
。将 [a]
类型的值展开为 a
类型的值是不可能的,除非输入恰好是长度为 1 的列表。在所有其他情况下,单子组合所做的事情比展开更有趣。
考虑 IO
。 getLine :: IO String
之类的值不包含 String
值。显然不可能打开包装,因为它没有包装任何东西。 IO
值的单子组合不会解包任何东西。它将 IO
个值组合成更复杂的 IO
个值。
我认为值得调整您对 Monad
含义的看法。如果它只是一个解包接口,那将毫无用处。不过,它更微妙。这是一个组合界面。
首先,您需要从问题中删除 monad。他们与此无关。将这篇文章作为对 monad 的一种观点,也许它不适合你,你可能在类型系统中仍然一知半解 haskell.
因此,您的问题可以改写为:为什么没有隐式转换 Just 5 => 5
?但答案很简单。因为值 Just 5
的类型是 Maybe Integer
,所以这个值可能是 Nothing
,但在这种情况下编译器必须做什么?只有程序员才能解决这种情况。
但是还有一个更不舒服的问题。有类型,例如 newtype Identity a = Identity a
。它只是一些价值的包装。那么,为什么没有隐式转换Identity a => a
?
简单的答案是 - 试图实现这一点会导致不同的系统类型,它不会有当前存在的许多优良品质。据此,可以为了其他可能性的利益而牺牲它。
My question is, what is so magical about the whole unwrapping thing?
这没什么神奇的。您可以使用普通的模式匹配(此处为 case
表达式的形式)来定义...
mapMaybe :: (a -> b) -> Maybe a -> Maybe b
mapMaybe f mx = case mx of
Just x -> Just (f x)
_ -> mx
... 这与 Maybe
的 fmap
完全相同。 Functor
class 添加的唯一东西——毫无疑问,这是一个非常有用的东西——是一个额外的抽象级别,涵盖了可以映射的各种结构。
Why do we distinguish
Just 5
from5
on programming language level?
比 Just 5
和 5
之间的区别更有意义的是它们 类型 之间的区别——例如在 Maybe Int
和 Int
之间。如果您有 x :: Int
,您可以确定 x
是一个您可以使用的 Int
值。但是,如果你有 mx :: Maybe Int
,你就没有这样的确定性,因为 Int
可能会丢失(即 mx
可能是 Nothing
),并且类型系统会强制你承认并处理这种可能性。
另请参阅:Maybe
(which isn't necessarily tied to classes such as Functor
and Monad
); Functor
和 Monad
(除了 Maybe
示例)。