ReaderT
. Can somebody give me a brief explanation, example or resource so I can get started? What is their purpose? Is there a benefit to using a ReaderTFuture
over a Reader[Future]
? I played a bit and can achieve the same with both (though the ReaderT is more convenient): http://goo.gl/J2J9lS. Am I even using it right? Is it merely a convenience?
Thinking about #1543, I became puzzled by something, and I was wondering if anyone has any insight:
Because it's easy to work with, many of our examples of lift
use add
, so me might define mAdd = lift(add)
. Well add
is a straightforward function, and it happens to be commutative, add(a, b) ≡ add(b, a)
. It simply seemed obvious that mAdd
would also be commutative. In simple cases, it is:
mAdd(Maybe.Just(10), Maybe.Just(32)); //=> Maybe.Just(42)
mAdd(Maybe.Just(32), Maybe.Just(10)); //=> Maybe.Just(42)
But it's definitely not:
R.lift(R.add)(Either.Right(10), Either.Left('oops')); //=> Either.Left('oops')
R.lift(R.add)(Either.Left('oops'), Either.Right(10)); //=> Either.Right(10)
My question is: Is my intuition leading me astray? Or should we expect that if fn
is commutative, then lift(fn)
is also commutative? If so, is there something wrong with lift
or one of its dependencies? Or is there something the matter with Either
?
Never mind. It clearly cannot be commutative:
R.lift(R.add)(Either.Left('oops'), Either.Left('a-daisy'));
So my intuition is off.
lift
doesn't do any kind of reflection or something, that Either
you're using looks broken.
ap
, map
and friends.
Monad
s, well more accurately it's only necessary with Chain
s. Really they should be called Chain Transformers, or Bind Transformers, or whatever the language uses.
Monad
and Chain
/Bind
, they have the name they have.
Chain
s is not necessarily another Chain
.
Functor
s, and compose them, the result is always a Functor
.
Apply
s, and Applicative
s.
Comp
osition data type that encoded this logic. Give it two composable data types, and you get Functor
, Apply
, Applicative
behavior for free.
// We can compose any two `Functor`s `x` and `y`
const Comp = x => ({
map: f => x.map(y => y.map(f)),
toString: () => x.map(y => y.toString()).toString(),
});
const None = {
map: _ => None,
toString: () => 'None',
};
const Some = x => ({
map: f => Some(f(x)),
toString: () => `Some(${x})`,
});
const Left = x => ({
map: _ => Left(x),
toString: () => `Left(${x})`,
});
const Right = x => ({
map: f => Right(f(x)),
toString: () => `Right(${x})`,
});
const someRight = Comp(Some(Right(3)));
console.log(`${someRight.map(x => x + 1)}`); //=> Some(Right(4))
const someLeft = Comp(Some(Left(3)));
console.log(`${someLeft.map(x => x + 1)}`); //=> Some(Left(3))
const noneRight = Comp(None);
console.log(`${noneRight.map(x => x + 1)}`); //=> None
map
once, rather than going down in there multiple times.
Comp
osing.
map
once.
ap
and of
as well.
chain
doesn't work.
chain
two data types.
const Id = x => ({
map: f => Id(f(x)),
toString: () => `Id(${x})`,
});
const nested = Comp(Some(Comp(Id(Comp(Right(Comp(Some(Some(3)))))))));
console.log(`${nested.map(x => x + 1)}`); //=> Some(Id(Right(Some(Some(4)))))
toString
to tell you it's a Comp
at each level.
Applicative
s compose, you could factor all that out similar to how ramda does compose
/pipe
. and just use of
a whole bunch of times.
Functor
or something.
toString
up there.
Setoid
, and have equality for free, if the two base data types are Functor
and Setoid
, for example.
That's awesome @joneshf, thanks for clearing a bunch of stuff up! :)
So Monad Transformers are a ways for a Chain to say: "Here's how you can compose my chain."
And that fills in the missing gap of monadic behaviours which are otherwise all composable?
And the reason you'd want to compose Monads is the same as for why you'd want to compose any other bits of code - to keep "dry"?
So it really is mainly a convenience for not having to mapmapmap over your nested containers?
I should get more accustomed with the theory behind monads, and the laws they follow. I've been learning to use them, in the API-sense, but I've yet yo fully understand them.
map(function operate(val){if(isObject(val)){return map(operate,val)};return val},obj)
, but is there a more elegant or Ramda-idiomatic way?
merge
that takes a transform function?
set
doing deep merging?
(...objs)=>reduce(set,{},objs)
, which is elegant enough imho
lift(fn)
should be commutative if fn
is? I haven't tried this with other versions of Either
for comparison. Perhaps I'll do that later.
require
d with Node?~/foo ❯❯❯ npm install ramda
/Users/Scott/foo
└── ramda@0.18.0
~/foo ❯❯❯ ls node_modules/ramda
BOOKMARKLET.md CHANGELOG.md LICENSE.txt README.md dist package.json src
~/foo ❯❯❯ node
> var R = require('ramda');
Error: Cannot find module 'ramda'
at Function.Module._resolveFilename (module.js:327:15)
at Function.Module._load (module.js:278:25)
at Module.require (module.js:355:17)
at require (internal/module.js:13:17)
at repl:1:9
at REPLServer.defaultEval (repl.js:252:27)
at bound (domain.js:281:14)
at REPLServer.runBound [as eval] (domain.js:294:12)
at REPLServer.<anonymous> (repl.js:417:12)
at emitOne (events.js:83:20)
require('./node_modules/X')
though
v5.0
export NODE_DEBUG=module
then when you start the repl and require, it will print your paths
node_modules
directory in your CWD
~/p/react-tmpl git:master ❯❯❯ export NODE_DEBUG=module ✖ ✱ ◼
~/p/react-tmpl git:master ❯❯❯ node ✖ ✱ ◼
> var R = require('ramda')
MODULE 29674: Module._load REQUEST ramda parent: <repl>
MODULE 29674: looking for "ramda" in ["/home/xananax/.node_modules","/home/xananax/.node_libraries","/usr/lib/node"]
NODE_DEBUG=module
env var is a handy trick to know.