这是一篇 Haskell 官网 文章 Functors, Applicatives, And Monads In Pictures 的 Swift 移植版本。
创作这篇文章的并不是我,我只是将它其中的代码翻译为 Swift ,这个过程很有趣。
如果你喜欢这篇文章,请感谢原作者 Aditya Bhargava,@_egonschiele
尽管关于 Swift 的宣传沸沸扬扬,但是它并不是一门真正的函数式语言。这意味着要实现一些 Haskell 内置的操作符功能,我们可能需要写一些额外的代码。
你可以在 GitHub 找到包含本文所有代码的 playground。
最后,如果你觉得这篇文章内容对于你来说很难理解不要担心,我也是在将原文读了许多遍之后才渐渐找到思路,而在翻译为 Swift 的过程中依然是一团糟。
这是一个简单的值(value):
我们也知道如何使用函数(function)来处理值:
这很容易懂,那么拓展一下,任意值都能在处于特定的上下文(context)中。你可以先想象上下文就像是一个盒子,你可以把值放进去。
现在当你使用函数处理这个值,根据上下文的不同会得到不同的结果。Functors, Applicatives, Monads,Arrows等概念都是基于此。Optional
类型定义了两种相关的上下文:
注意:图上的 Maybe(Just | None)来自 Haskell,类似于 Swift 的 Optional,
.Some
和.None
。
1 2 3 4 |
enum Optional<T> { case None case Some(T) } |
紧接着我们将看到一个值的类型是 .Some(T)
或者是 .None
会怎样造成函数作用的不同。我们先来谈谈 Functors!
Functors
当一个值被封装到盒子里,一个普通的函数无法作用于它:
这个是就是 map
的由来(在 Haskell 是 fmap
)。map
知道如何使用函数处理数据类型。例如,你想要使用一个函数,将 .Some(2)
加 3。使用 map
:
1 2 3 4 5 6 |
func plusThree(addend: Int) -> Int { return addend + 3 } Optional.Some(2).map(plusThree) // => .Some(5)<br> |
或者用更简洁的语法,使用 Swift 的 autoclosure:
1 2 |
Optional.Some(2).map { $0 + 3 } // => .Some(5)<br> |
砰! map
的作用我们看到了,但是它是怎么做到的?
到底什么是 Functor?
任意定义了 map
( Haskell 中的 fmap
)如何作用于自己的类型都是 Functor,map
是这样作用的:
所以我们可以这么做
1 2 |
Optional.Some(2).map { $0 + 3 } // => .Some(5)<br> |
map
神奇地使函数起了作用,因为 Optional
是一个 Functor。它表明了 map
是如何应用 Some
和 None
。
1 2 3 4 5 |
func map<U>(f: T -> U) -> U? { switch self { case .Some(let x): return f(x) case .None: return .None }<br> |
Optional.Some(2).map { $0 + 3 }
:
这里是当我们写下 Optional.Some(2).map { $0 + 3 }
背后所发生的:
所以我们就像在说,map
,请将 { $0 + 3 }
作用与 .None
上?
1 2 |
Optional.None.map { $0 + 3 } // => .None<br> |
就像黑客帝国中的 Morpheus,map
知道要做什么;开始时是 None
,结束也是 None
!map
是一种禅。现在你可以理解为什么 Optional
类型的存在。举个例子,对于没有 Optional
类型的语言,比如 Ruby,对于一条数据库记录是这么工作的:
1 2 3 4 5 6 |
let post = Post.findByID(1) if post != nil { return post.title } else { return nil } |
但是用 Swift 使用 Optional
仿函数: