Reader Monad的本地功能的目的是什么?

时间:2018-01-19 17:29:39

标签: javascript

简而言之,Reader Monad只是一个函数的包装器。通过计算隐式传递配置信息是很好的:



const ap = f => x => f(x);
const id = x => x;
const co = x => y => x;
const comp = f => g => x => f(g(x));

const Reader = ap(cons => f => {
  const t = new cons();
  t.run = x => f(x);
  return t;
}) (function Reader() {});

Reader.map = f => tf => Reader(comp(f) (tf.run));
Reader.ap = af => ag => Reader(x => af.run(x) (ag.run(x)));
Reader.chain = mf => fm => Reader(x => fm(mf.run(x)).run(x));
Reader.of = f => Reader(co(f));
Reader.ask = () => Reader(id);
Reader.asks = f => Reader.chain(Reader.ask) (x => Reader.of(f(x)));
Reader.local = f => tf => Reader(comp(tf.run) (f));

const compM = tDict => fm => gm => x =>
  tDict.chain(tDict.chain(tDict.of(x)) (gm)) (fm);

const foo = n => Reader(env => (console.log(env), n + 1)),
  c = compM(Reader) (foo) (foo) (2);

console.log(
  c.run("shared environment")
);




然而,我无法找到local的好例子,我缺乏想象力如何使用它。我知道local只是contramap,但这并没有多大帮助。那么它的目的是什么?

1 个答案:

答案 0 :(得分:0)

  

我理解local只是contramap,但这并没有多大帮助。那么它的目的是什么?

contramapmap一样重要 - 在the example Bergi linked(此处翻译成JS)中,local允许影响之前的值 >下一位读者得到它

const calculateLength =
  Reader.map (s => s.length) (Reader.ask ())

const calculateModifiedLength = 
  Reader.local (s => 'Prefix ' + s) (calculateLength)

console.log (calculateLength.run ('12345'))
// '12345'.length == 5
// => 5    

console.log (calculateModifiedLength.run ('12345'))
// 'Prefix 12345'.length == 12
// => 12

这是你的读者monad,精心重新格式化。我包含了每个函数的演示,以便我们可以验证每个结果是否正确。谢谢你让我终于了解这个有趣的monad!

const Reader = f =>
  ({ run : x => f (x) })

Reader.of = x =>
  Reader (() => x)

Reader.chain = f => m =>
  Reader (x => f (m.run (x)) .run (x))
  
Reader.map = f => m =>
  Reader (x => f (m.run (x)))

Reader.ap = f => m =>
  Reader (x => f.run (x) (m.run (x)))
  
Reader.join = m =>
  Reader (x => m.run (x) .run (x))

Reader.ask = () =>
  Reader (identity)
  
Reader.local = f => m =>
  Reader (x => m.run (f (x)))

const identity = x => x
const sq = x => x * x
const add = x => y => x + y

const calculateLength =
  Reader.map (s => s.length) (Reader.ask ())

const calculateModifiedLength = 
  Reader.local (s => 'Prefix ' + s) (calculateLength)
  
console.log
  ( Reader.chain (x => Reader.of (x + 1)) (Reader (sq)) .run (4) // 17    sq (4) + 1
  , Reader.chain (x => Reader (add (x))) (Reader (sq)) .run (4)  // 20    sq (4) + 4
  , Reader.map (sq) (Reader (sq)) .run (4)                       // 256   sq (sq (4))
  , Reader.ap (Reader.of (sq)) (Reader.of (4)) .run ()           // 16    sq (4)
  , Reader.ap (Reader (add)) (Reader (sq)) .run (4)              // 20   sq (4) + 4 
  , Reader.join (Reader.of (Reader.of (4))) .run ()              // 4
  , Reader.ask () .run (4)                                       // 4
  , calculateLength .run ('12345')                               // 5    '12345'.length
  , calculateModifiedLength .run ('12345')                       // 12   'Prefix 12345'.length
  )

这是类型签名,对于那些感兴趣的人

Reader :: (e -> a) -> Reader e a
Reader.of :: a -> Reader e a 
Reader.chain :: (a -> Reader e b) -> Reader e a -> Reader e b
Reader.map :: (a -> b) -> Reader e a -> Reader e b  
Reader.ap :: Reader e (a -> b) -> Reader e a -> Reader e b
Reader.join :: Reader e (Reader e a) -> Reader e a   
Reader.ask :: () -> Reader a a
Reader.local :: (e -> e) -> Reader e a -> Reader e a