Open antonklimov opened 4 years ago
how is this supposed to work? Array does not have .sequence.
It is supposed to work because you only need the internal object to be an Applicative to write sequence / traverse. Here, the array ought to be treated as a Foldable and the Either a
as the Applicative. Yet, I've just checked and indeed, the definition of the pointfreesequence
is bonkers at the moment.
First is again with array being treated as a monad.
Nope, array is being treated as a Foldable.
And second that join which is defined in support\index.js is a monadic join
Right! We have a name clash here, and this one should be intercalate
It seems that sequence
can be defined as:
const sequence = curry((of, f) => traverse(of, identity, f));
and traverse
can be made aware of arrays like:
const traverse = curry((of, fn, f) =>
Array.isArray(f)
? f.reduce((innerF, a) => fn(a).map(append).ap(innerF), of([]))
: f.traverse(of, fn)
);
Would that work?
Yes indeed! That sounds a bit more sound that extending the Array prototype with a "traverse" method ^.^
@antonklimov Even when I changed the array with the snippet you put. I still can't make example work. I get a buffer but I try to partially the readfile with the encoding suddenly the readfile task blows up. I am sure I am doing something wrong. This might be unrelated so sorry if the case. Here is the example.
const readFile = filename => new Task((reject, result) => {
readFile(filename, (err, data) => (err ? reject(err) : result(data)));
});
// readDir :: String -> Task Error (List String)
const readDir = path => new Task((reject, result) => {
fs.readdir(path, (err, data) => (err ? reject(err) : result(data)));
});
// readFirst :: String -> Task Error (Maybe String)
const readFirst = compose(
chain(traverse(Task.of, readFile('utf-8'))),
map(safeHead),
readDir,
);
The little readfile I could not find in the book by the way. if there supposed to be one.
This is not related to array, and first of all, in support.js there are the following definitions:
const readdir = function readdir(dir) {
return Task.of(['file1', 'file2', 'file3']);
};
const readfile = curry(function readfile(encoding, file) {
return Task.of(`content of ${file} (${encoding})`);
});
Your readFile
takes only 1 parameter and is not curried. So readFile('utf-8')
is not a function which is required in traverse
but rather a Task
with "utf-8" treated as a file name. Also note that for fs.readFile
(I suppose that is what you wanted to use) the name is the first, and the option will be the second, which you did not provide in your definition. Another problem with your code is that rs.readdir
can return subdirectories and fs.read will fail on that. So you would need to change your definition of readdir
to filter out the directories. The easiest fix for readFile
would be:
const readFile = (encoding) => (filename) =>
new Task((reject, result) => {
fs.readFile(filename, encoding, (err, data) =>
err ? reject(err) : result(data)
);
});
oh my.. I have spend hours looking at this and I swear it melt my brains up to the point where I have to put my laptop aside to regain sanity.
I will try to break it down, for you and for myself.
The disturbing lines in chapter 12 is this:
Let's rearrange our types using
sequence
:sequence(List.of, Maybe.of(['the facts'])); // [Just('the facts')] sequence(Task.of, new Map({ a: Task.of(1), b: Task.of(2) })); // Task(Map({ a: 1, b: 2 })) sequence(IO.of, Either.of(IO.of('buckle my shoe'))); // IO(Right('buckle my shoe')) sequence(Either.of, [Either.of('wing')]); // Right(['wing']) sequence(Task.of, left('wing')); // Task(Left('wing'))
// sequence :: (Applicative f, Traversable t) => (a -> f a) -> t (f a) -> f (t a)
const sequence = curry((of, f) => f.sequence(of));
sequence(List.of, Maybe.of(['the facts'])), // [Just('the facts')]
This makes total sense. The List
is an ADT for Array
. Maybe.of (Array (String))
becomes Just (Array (String))
, which gets turned inside out by sequence
to Array (Just (String))
. The last type could also be written as [Just (String)]
.
Actually, I am going to skip line 2 because that is the one I understand the least...
sequence(IO.of, Either.of(IO.of('buckle my shoe'))); // IO(Right('buckle my shoe'))
console.log( sequence(IO.of, Either.of(IO.of('buckle my shoe'))).unsafePerformIO() ) // TypeError
Again, this makes total sense but this time we get a TypeError
- Cannot read property 'unsafePerformIO' of undefined.
Turns out that Right.traverse is missing a return
(I got a PR for that).
With the return
added, we get Right('buckle my shoe')
.
IO.of ('buckle my shoe')
returns the Applicative Functor IO
which holds a String
.Either.of (IO (String))
returns Right (IO (String))
.sequence
's arguments are IO.of
==> x => new IO(() => x)
and Right (IO (String))
.sequence
is then, Right (IO (String)).sequence(x => new IO(() => x))
.Right.sequence
's body is then, this.traverse(x => new IO(() => x), identity)
.Right.traverse
does not use the first argument but only the second argument fn
, which is identity
.
So the body becomes, identity(this.$value).map(Either.of)
==> IO (String).map(Either.of)
IO.map
becomes, new IO (() => Either.of (IO (String).unsafePerformIO()))
.The tranformation is (new IO (() => Either.of (IO (String).unsafePerformIO()))).unsafePerformIO()
==> Right (String)
.
When we execute this newly formed IO
we execute the following steps:
(new IO (() => Either.of (IO (String).unsafePerformIO()))).unsafePerformIO()
Either.of (IO (String).unsafePerformIO())
Either.of (String)
Right (String)
(new IO (() => Right (String))).unsafePerformIO()
IO (Right (String)).unsafePerformIO()
Right (String)
sequence(Either.of, [Either.of('wing')]), // TypeError
As noted in the original post, that line does not make sense.
It could be sequence(List.of, Either.of(['wing'])) // [Right('wing')]
<- Invert list in Either to List (Right('wing'))
.
Or sequence(Either.of, new List([Either.of("wing")]))
<- Invert list of Either
s to Either ({$value: ['wing]})
. The latter introduced an object, which holds a List (String)
.
Something is afoul here. Perhaps a bug? I now see that List (a)
will print a
as {$value: }
via [util.inspect.custom]
, regardless of the actual Type it holds. So Right(2)
will show as {$value: 2}
etc.
[util.inspect.custom]() {
return `List(${inspect(this.$value)})`;
}
I think line 4 is suppose to be:
sequence(List.of, Either.of(['wing'])), // List (Right('wing'))
// left :: a -> Either a b
const left = a => new Left(a);
sequence(Task.of, left('wing')), // Task(Left('wing'))
sequence(Task.of, left('wing'))
.fork(console.error.bind(console, 'Error:'), console.log), // Left('wing')
Line 5, makes total sense but the purpose evades me. Why would I want to use sequence
to wrap Left (String)
in a Task
?
Task.of(left('wing'))
will yield the exact same result but much cleaner and faster.
sequence(Task.of, new Map({ a: Task.of(1), b: Task.of(2) })), // Task(Map({ a: 1, b: 2 }))
// but..
sequence(Task.of, new Map({ a: Task.of(1), b: Task.of(2) }))
.fork(console.error.bind(console, 'Error:'), console.log), // TypeError: Map.of is not a function
It's important to note that Map
is shadowed by class Map
in support/index.js.
I will not try to outline the execution here. When I look at the class, I see that Map.insert
is using Map.of
that does not exist. It makes me think that this class has never actually been executed but is a piece of theory that I can not hope to use and experiment with.
It would be super helpful, in learning the concept of Traversable and being comfortable using Traversables, if the examples would work as advertised. I'm still not knowledgeable enough to make this work on my own and I feel stuck in an otherwise great book on ADTs.
With the exception of line 5; I think I understood it all now. Please see #605 and correct me, if I'm wrong.
sequence(Either.of, [Either.of('wing')]); // Right(['wing'])
: how is this supposed to work? Array does not have.sequence
.sequence(Either.of, new List([Either.of("wing")]))
might work here.Later
Two problems here. First is again with array being treated as a monad. And second that
join
which is defined in support\index.js is a monadic joinwhereas here the join needs to be an array join like: