美文网首页工作生活
【翻译】Mostly Adequate Guide to Fun

【翻译】Mostly Adequate Guide to Fun

作者: monvhh | 来源:发表于2019-06-29 21:34 被阅读0次

原文地址:https://mostly-adequate.gitbooks.io/mostly-adequate-guide/

前十章中文翻译:https://llh911001.gitbooks.io/mostly-adequate-guide-chinese/

说明:

  • 编程/计算机基础较弱,理解不到原作者的深度,翻译一定会有问题,尽量在自己成长的同时保持更新。
  • 英语也弱,try my best,也是保持更新/修复。
  • 只讲重点,不会像前十章翻译那样直译,觉得无关紧要的引用或叙述,统统做总结简述。

第11章 自然转换

诅咒嵌套

此嵌套指:两种以上的类型将一个值包裹起来,如下

Right(Maybe('b'));

IO(Task(IO(1000)));

[Identity('bee thousand')];

一个情景喜剧

// getValue :: Selector -> Task Error (Maybe String)
// postComment :: String -> Task Error Comment
// validate :: String -> Either ValidationError String

// saveComment :: () -> Task Error (Maybe (Either ValidationError (Task Error Comment)))
const saveComment = compose(
  map(map(map(postComment))),
  map(map(validate)),
  getValue('#comment'),
);

有多种方案解决这个常见的问题:将多重类型组合成一个巨大的容器、排序和join、同质化、解构等等。这章我们集中在通过自然转换同质化它们。

纯天然

自然转换是函子间的态射,意即:作用在容器上的函数。它是一个函数:(Functor f, Functor g) => f a -> g a。特别之处在于,无论如何,我们不能偷看函子的内容。把它想象成高级机密的交换。这是一个结构上的操作,一个函子的变装。正式来说:自然转换是使以下成立的任意函数。

naturalTransformation.png
用代码表示如下:
// nt :: (Functor f, Functor g) => f a -> g a
compose(map(f), nt) === compose(nt, map(f));

示意图和代码都说明了同一件事情,先自然转换然后map或者先map然后再自然转换,可以得到同样的结果。附带说明,这来自自然定理,只不过自然转换(和函子)没有限制函数的类型。

坚持原则的类型转换

程序员都很熟悉类型转换。我们将Strings转换成Boolens,Integers转换成Floats(JS中仅有Numbers)。不同之处在于,这里我们在处理代数容器,并且用一些理论去处理。
先看一些例子

// idToMaybe :: Identity a -> Maybe a
const idToMaybe = x => Maybe.of(x.$value);

// idToIO :: Identity a -> IO a
const idToIO = x => IO.of(x.$value);

// eitherToTask :: Either a b -> Task a b
const eitherToTask = either(Task.rejected, Task.of);

// ioToTask :: IO a -> Task () a
const ioToTask = x => new Task((reject, resolve) => resolve(x.unsafePerform()));

// maybeToTask :: Maybe a -> Task () a
const maybeToTask = x => (x.isNothing ? Task.rejected() : Task.of(x.$value));

// arrayToMaybe :: [a] -> Maybe a
const arrayToMaybe = x => Maybe.of(x[0]);

看出来没?将一个函子转换成另一个函子,我们可以在过程中丢失一些信息,只要保证没有在形式转换中丢失将要map的value即可。这就是关键点:根据我们的定义,map必须可以持续下去,甚至是在转换结束之后,还可以继续。

从转换效果的角度观察,ioToTask可以认为是同步到异步的转换,arrayToMaybe是非确定性到可能失败。注意在JS中我们不能从异步转换到同步,所以我们不能写taskToIO - 那将是超自然转换。

Feature Envy

假设我们需要再List上使用其他类型的特性,比如sortBy。自然转换提供一个很好的方式转换成目标类型。

// arrayToList :: [a] -> List a
const arrayToList = List.of;

const doListyThings = compose(sortBy(h), filter(g), arrayToList, map(f));
const doListyThings_ = compose(sortBy(h), filter(g), map(f), arrayToList); // law applied

同构Javascript

当可以完全的来回调用而不丢失任何信息,这就是同构。
如果我们可以提供自然转换作为证据,我们就可以说两种类型是同构的。

// promiseToTask :: Promise a b -> Task a b
const promiseToTask = x => new Task((reject, resolve) => x.then(resolve).catch(reject));

// taskToPromise :: Task a b -> Promise a b
const taskToPromise = x => new Promise((resolve, reject) => x.fork(reject, resolve));

const x = Promise.resolve('ring');
taskToPromise(promiseToTask(x)) === x;

const y = Task.of('rabbit');
promiseToTask(taskToPromise(y)) === y;

Promise和Task是同构的,我们也可以写一个listToArray实现arrayToList显示他们是同构。作为一个反例,arrayToMaybe不是同构,因为它丢失了信息。

// maybeToArray :: Maybe a -> [a]
const maybeToArray = x => (x.isNothing ? [] : [x.$value]);

// arrayToMaybe :: [a] -> Maybe a
const arrayToMaybe = x => Maybe.of(x[0]);

const x = ['elvis costello', 'the attractions'];

// not isomorphic
maybeToArray(arrayToMaybe(x)); // ['elvis costello']

// but is a natural transformation
compose(arrayToMaybe, map(replace('elvis', 'lou')))(x); // Just('lou costello')
// ==
compose(map(replace('elvis', 'lou'), arrayToMaybe))(x); // Just('lou costello')

它们确实是自然转换,因为在任何一方map都会产出同样的结果。

更广泛的定义

这些结构化的函数并不限制通过任何方式进行类型转换。
这有几个不同的例子。

reverse :: [a] -> [a]

join :: (Monad m) => m (m a) -> m a

head :: [a] -> a

of :: a -> f a

自然转换规则也支持这些函数。

一个嵌套的解决方案

第12章

相关文章

网友评论

    本文标题:【翻译】Mostly Adequate Guide to Fun

    本文链接:https://www.haomeiwen.com/subject/ljmpcctx.html