Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pt3 #357

Merged
merged 7 commits into from Dec 30, 2017
Merged

Pt3 #357

merged 7 commits into from Dec 30, 2017

Conversation

DrBoolean
Copy link

@KtorZ Here is the start of pt3 :)

@KtorZ
Copy link
Member

KtorZ commented Dec 23, 2017

:D ... Wonderful!

I'll go through it asap and most probably merge it on top of the reviewed exercises & es6.

@DrBoolean
Copy link
Author

:) I know it's the holidays. Certainly no rush at all. Going for like 3-4 more chapters and calling it a book.

@snackycracky
Copy link

wow great, that comes under my christmas tree

@as02700
Copy link

as02700 commented Dec 25, 2017

amazing...

@Fabs Fabs mentioned this pull request Dec 28, 2017
@gyzerok
Copy link
Contributor

gyzerok commented Dec 28, 2017

Best Xmas gift this year!

@wayneseymour
Copy link

wayneseymour commented Dec 28, 2017 via email

Copy link
Member

@KtorZ KtorZ left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few typos noticed from a first read. Haven't been through exercises or laws in depth yet.

ch11.md Outdated
[Identity('bee thousand')]
```

Until now, we've managed to evade this common scenario with carefully crafted examples, but in practice, as one codes, types tend to tangle themselves up like earbuds in an exercism. If we don't meticulously keep our types organized as we go along, our code will read hairier than a beatnik in a cat café.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'exercism' => not sure if I lack a bit of vocabulary on that one or if it's simply a typo ^.^

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for looking this over @KtorZ!

It's an allusion to the movie "The Exorcist" - my spelling is incorrect indeed

ch12.md Outdated
@@ -0,0 +1,256 @@
# Chapter 11: Traversing the Stone
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

^__ Chapter 12

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ha. I'm working on like 4 chapters at once here... Good catch

ch12.md Outdated

```js
// sequence :: (Traversable t, Applicative f) => (a -> f a) -> t (f a) -> f (t a)
const sequence = (point, x) => x.sequence(point)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currification forgotten here (unfortunately not by default in JavaScript :'( ...)

const sequence = curry((point, x) => x.sequence(point));

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yes. I definitely need that as I use it in the curried form later.

}
```

Ah yes, if our `__value` is a functor (it must be an applicative, in fact), we can simply `map` our constructor to leap frog the type.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about renaming point to pure ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ya know, I thought about that. Perhaps the best one would be of since we've already covered Pointed.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

of might be a better idea indeed to maintain consistency with previous chapters. The laws from this chapter also refer to of.

👍

ch12.md Outdated
// Identity(Right('stuff'))
```

This should be straight forward. If we place an `Identity` in our functor, then turn it inside out with `sequence` that's the same as just placing it on the outside to begin with. We chose `Right` as our guinea pig as it is easy to try the law and inspect. An arbitrary functor there is normal, however, the use of a concrete functor here, namely `Identity` in the law itself might raise some eyebrows. Remember a [category](ch5.md#category-theory) is defined by morphisms between its objects that have associative composition and identity. When dealing with the category of functors, natural transformations are the morphisms and `Identity` is, well identity. The `Identity` functor is as fundamental in demonstrating laws as our `compose` function. In fact, we should give up the ghost and follow suit with our [Compose](ch8.md#a-spot-of-theory) type:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

*straight forward -> straightforward ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

ch12.md Outdated

## In summary

*Traversable* is a poweful interface that gives us the ability to rearrange our types with the ease of a telekinetic interior decorator. We can achieve different effects with different order as well as iron out those nasty type wrinkles that keep us from `join` them down. Next, we'll take a bit of a detour to see one of the most powerful interfaces of functional programming and perhaps even algebra itself: [Monoids bring it all together](ch13.md)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

*poweful -> powerful

*different order -> different orders

*join -> joining

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@KtorZ
Copy link
Member

KtorZ commented Dec 28, 2017

I haven't got time to go through exercises nor to apply the airbnb style yet. I'll on Saturday, and will also fix some of the typos I found on a first read (unless you do it in the meantime ^.^ ...)

Hopefully merged somewhere before next year.

@DrBoolean
Copy link
Author

Thank ya sir! Again, no rush at all and I'll try to get to it if I have time myself.

ch12.md Outdated

Ah yes, if our `__value` is a functor (it must be an applicative, in fact), we can simply `map` our constructor to leap frog the type.

You may have noticed that we've ignored the `point` entirely. It is passed in for the occasion where mapping is futile, as is the case with `Left`:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HA. there is a way to set up the there. "ignored the point entirely" is way to good not to capitalize on.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there's a word missing in your comment, if not, I am not sure to understand what you meant :s ..

Copy link

@evilsoft evilsoft Dec 29, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow. maybe like a few words, goodness!

Should read like:
"HA. I am sure there is a way to set up a pun here. "ignored the point entirely" is way to good not to capitalize on.

ch12.md Outdated

```js
// sequence :: (Traversable t, Applicative f) => (a -> f a) -> t (f a) -> f (t a)
const sequence = (point, x) => x.sequence(point)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So glad you went with the pointed function over TypeRep of an Applicative. I find this interface a lot more handy and natural.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:) Thanks for looking this over, btw!

Copy link
Member

@KtorZ KtorZ left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some suggestions on the laws of the chapter 12.

ch12.md Outdated

comp2(Right, Array)(Identity(Right([true])))
// Compose(Right([Id(true)]))
```
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very small thing here but Identity is referred as Id in the comments. I suggest to stick with Identity for consistency.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ha, yeah. Copied from a local js file and missed that rename.

ch12.md Outdated

```js
const comp1 = compose(sequence(Compose.of), map(Compose.of))
const comp2 = (point1, point2) => compose(Compose.of, map(sequence(point2)), sequence(point1))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think of:

  • showing a quick recap of Compose
  • using f and g instead of point1 and point2 to make the parallel with Compose a bit more obvious
  • mentioning that f and g (or point1 and point2) need to be Traversable (even though implied by the use of sequence) ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great ideas!

ch12.md Outdated

```js
const natLaw1 = (point, nt) => compose(nt, sequence(p))
const natLaw2 = (point, nt) => compose(sequence(p), map(nt))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same consideration from above about using f instead of point (or p with the typo ^.^).

ch12.md Outdated

So far, in our cirque du conteneur, you've seen us tame the ferocious [functor](ch8.md#my-first-functor), bending it to our will to perform any operation that strikes our fancy. You've been dazzled by the juggling of many dangerous effects at once using function [application](ch10.md) to collect the results. Sat there in amazement as containers vanished in thin air by [joining](ch9.md) them together. At the side effect sideshow, we've seen them [composed](ch8.md#a-spot-of-theory) into one. And most recently, we've ventured beyond what's natural and [transformed](ch11.md) one type into another before your very eyes.

And now for our next trick, we'll look at traversals. We'll watch types soar over one another as if they were trapeze artists holding our value in tact. We'll reorder effects like the trolleys in a tilt-a-whirl. When our containers get intertwined like the limbs of a contortionist, we can use this interface to straighten things out. We'll witness different effects with different orderings. Fetch me my pantaloons and slide whistle, let's get started.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as if they were trapeze artists holding our value in tact.

Should in tact be intact. Or are you making reference to the heated debate all over the internets about the etymology.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💯

const findUser = x => x > 0 ? Task.of(Either.of({id: x, name: 'userface'})) : Task.of(new Left('not found'))

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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

either is not defined.
Did you want to bring it in or should it have been created with exercise 1?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's part of support.js and we use it in the exercises somewhere. I think we should just add the type signature in a comment at the top or something...

I can't believe how "old school" the module system with globals and such are in the code itself. Might be a better way to actually link to the function from here.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is going to change with the upcoming restructuring update 👍

@MostlyAdequate MostlyAdequate deleted a comment from DrBoolean Dec 29, 2017
ch11.md Outdated
const arrayToMaybe = x => Maybe.of(x[0])
```

See the idea? We're just changing one functor to another. We are permitted to lose information along the way so long as the value we'll `map` doesn't get lost in the shape shift shuffle. That is the thing here, remember, `map` must carry on after the transformation according to our defining law.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is the thing here, remember, map must carry on after the transformation according to our defining law.

Entirely possible that this is just because i'm not a native speaker but i have read the sentence several times now and I still find it confusing - I know what you are saying but the sentence structure is weird to me ;-)

Copy link
Member

@KtorZ KtorZ Dec 30, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe some punctuation will help, also by moving ahead the end of the sentence. Something like:

That is the thing here, remember. Map must carry on, according to our definition, even after the transformation.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i dig that!

ch12.md Outdated
}
```

We'd like the types to always end up in the same arrangement, therefore it is necessary for types like `Left` who don't actually hold our inner applicative to get a little help in doing so. The *Applicative* interface requires that we first have a *Pointed Functor* so we'll always have a `point` to pass in. In a language with a type system, the outer type can be inferred from the signature and does not need to be explicitly given.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually hold our inner applicative

Should this be Applicative?
Or is it correct as is because it references an actual Applicative instance?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think both are correct depending on the context. My own interpretation is that Applicative refers to the concept / interface whereas applicative refers to an instance of that concept. I'll suggest to keep it with an uppercase as well as other terminologies of the same kind (Monad, Functor, Natural Transformation etc.) ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, was trying to keep it to the instance/class definition like "this applicative is and instance of Applicative". I don't think i'm consistent though... Caps for everything is fine by me

}
```

Ah yes, if our `__value` is a functor (it must be an applicative, in fact), we can simply `map` our constructor to leap frog the type.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(it must be an applicative, in fact)

Should this be Applicative?

ch12.md Outdated
As a natural consequence of the above law, we get the ability to fuse traversals:

```js
traverse(compose(Compose.of, map(compose(f , g)))) = compose(Compose.of, map(traverse(of, f)), traverse(of, g))
Copy link

@evilsoft evilsoft Dec 29, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The left traverse seems to be missing the constructor for it's first.

should this be something like:

traverse(Compose.of, compose(Compose.of, map(compose(f , g)))) = compose(Compose.of, map(traverse(of, f)), traverse(of, g))

edit @KtorZ: removed emojis from message

Copy link
Member

@KtorZ KtorZ Dec 30, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed. Plus, I believe the law is erroneous. Let's switch to PureScript to lose some verbosity and gain some expressiveness, we currently have this:

traverse (Compose . map (f . g)) = Compose . map (traverse f) . traverse g

On the left, it suggests that:

g :: a -> b
f :: b -> f c
Compose :: h (f c) -> Compose h f c

and on the right:

g :: a -> g b
f :: b -> f c
Compose :: g (f c) -> Compose g f c

which doesn't hold well. I believe it would rather be:

traverse (Compose . map f . g) = Compose . map (traverse f) . traverse g

-- or even, to have (Compose f g) instead of (Compose g f)

traverse (Compose . map g . f) = Compose . map (traverse g) . traverse f

and, translated to the js version:

traverse(Compose.of, compose(Compose.of, map(g), f)) === compose(Compose.of, map(traverse(of$2, g)), traverse(of$1, f))

// where:
// of$1 :: a -> f a
// of$2 :: b -> g b

@DrBoolean, do you confirm ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh, yes, sorry. That one was never verified, just loosely translated in a hurry. Nice work on the Purs proof ;)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So i translated the fusion law from https://www.cs.ox.ac.uk/jeremy.gibbons/publications/iterator.pdf

NO CLUE WHERE I GOT THE map?! wtf.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just provided a link here instead of writing it out.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed:

image

Also confirmed with hackage's description of Traversable ^.^

### Composition

```js
const comp1 = compose(sequence(Compose.of), map(Compose.of))

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh gosh, I agree with @KtorZ for dat recap.
Also I know this is a REALLY big can of worms (like how does one implement really implement Compose.of without a type system), BUT maybe showing ap and Compose.of implementations (I know that could be its own huge section.). Although it may point out some of the shortcomings that surround traversals in JS and take a lot of focus away from the point here.

That is tricky.

Also should this be:

const comp1 = compose(sequence(Compose.of), map(Compose))

wouldn't that Compose.of throw in a F(G(F(G(x))))
I did not code it out yet though so I am probably wrong.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also coming with the restructuring. These implementation details are now provided in an appendix. Compose.of is a bit tricky to define though in JavaScript. Ideally, we would need to specify the 2 underlying functors' constructors but we can't change the signature of of (it will defy its entire purpose, even though being, imo, acceptable for this edge case). The way @DrBoolean solved it in the examples was to directly provide a value nested in 2 functors:

Compose.of(Identity.of(Either.of(true)));

However, we would ideally like to do is something like:

Compose.of(true); // Identity(Right(true))

Yet, for the same reasons sequence and traverse have an extra argument, we can't write this as such in JavaScript like we would in Haskell. We could go for a "factory-like" method as such:

class Compose {
  static getOf(f, g) {
    return x => new this(f(g(x)));
  }

  constructor(x) {
    this.$value = x;
  }
}

const of = Compose.getOf(Identity.of, Either.of);

of(true); // Compose(Identity(Right(true)))

or simply use optional arguments to provide constructors in that case:

class Compose {
  static of(x, f=Identity.of, g=Identity.of) {
    return new Compose(f(g(x)));
  }

  constructor(x) {
    this.$value = x;
  }
}

Compose.of(true, Identity.of, Either.of); // Compose(Identity(Right(true)))

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's also a closure approach:

const createCompose = (F, G) => {
return class Compose { 
  static of(x) {
    return new Compose(F(G(x)));
  }

  constructor(x) {
    this.$value = x;
  }
}
}
const Compose = createCompose(Id, Either)
Compose.of(true, Identity.of, Either.of); // Compose(Identity(Right(true)))

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the closure approach better and will stick with that for the upcoming appendix 👍

ch12.md Outdated

// test with a random natural transformation and our friendly Identity/Right functors.

// maybeToEither :: Identity a -> Either () a
Copy link

@evilsoft evilsoft Dec 30, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like there may be some artifacts from copy/pasting

should it look something like:

const natLaw1 = (p, nt) => compose(nt, sequence(p))
const natLaw2 = (p, nt) => compose(sequence(p), map(nt))

// maybeToEither :: Maybe a -> Either () a
const maybeToEither = m =>
  m.either(Left, Right)

natLaw1(Maybe.of, maybeToEither)(Identity(Maybe.of('barlow one')))
// Right(Identity('barlow one'))

natLaw2(Either.of, maybeToEither)(Identity(Maybe.of('barlow one')))
// Right(Identity('barlow one'))

edit @KtorZ: removed emojis from message

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe there's indeed some copy/pasting issues here ^.^ ... though m.either doesn't make much sense since we have m :: Maybe a. I would rather go for:

const natLaw1 = (of, nt) => compose(nt, sequence(of))
const natLaw2 = (of, nt) => compose(sequence(of), map(nt))

// test with a random natural transformation and our friendly Identity/Right functors.

// maybeToEither :: Maybe a -> Either () a
const maybeToEither = x => x.__value ? Right(x.__value) : Left()

natLaw1(Maybe.of, maybeToEither)(Identity.of(Maybe.of('barlow one')))
// Right(Identity('barlow one'))

natLaw2(Either.of, maybeToEither)(Identity.of(Maybe.of('barlow one')))
// Right(Identity('barlow one'))

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ha, yeah, that should be either(Left, Right, m), but i like yours better.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah sorry that was an artifact from my crocks lib when I was giving it a play. mah bad.

ch12.md Outdated
// Right(Identity('barlow one'))
```

This is similar to our identity law. If we first swing the types around then run a natural transformation on the outside, that should equal mapping a natural transformation, then flipping the types. The natural transformation to ensures our structure does not fiddle with its contents or fall apart on us during traversal.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The natural transformation to ensures our structure

Think there may be an extra word or two in there

@KtorZ KtorZ self-assigned this Dec 30, 2017
@DrBoolean
Copy link
Author

Haha, k! This is awesome. How about i make the changes today, then we open new issues for anything past this?

@KtorZ
Copy link
Member

KtorZ commented Dec 30, 2017

Agreed. I wasn't really expecting these amount of reactions ^.^, but let's do as we've always done so far: open small issues for small changes / typos / revisions etc.

Let me know once pushed 👍, I'll at least merge it as is and later on add the ES6/ES7 and restructuring revisions on top shortly after but at least, people would be able to read the chapters properly in the meantime!

@DrBoolean
Copy link
Author

Pushed!

Copy link
Member

@KtorZ KtorZ left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@KtorZ KtorZ merged commit c058358 into master Dec 30, 2017
@evilsoft
Copy link

HAHA sorry about that.
But do not sweat it. I will cease at this point.
I have a rule to NEVER provide 👀 on a project that edits comments, unless it breaks COC.
So this project has violated that, which is a REAL bummer.

@crusoexia
Copy link

Cheers! I love this book very much!

@DrBoolean
Copy link
Author

@evilsoft I'm confused and want to make it not a bummer. What did you mean by "edits comments"? Also, holy smokes we do need a CoC!

@KtorZ
Copy link
Member

KtorZ commented Dec 31, 2017

@DrBoolean , It seems that @evilsoft has a kind of plugin that automatically replace text with emojis when "appropriated". Therefore, I did edit a couple of his comments (leaving an explicit note: "Edited by KtorZ: removed inserted emojis") as I judged that

  • "Looks like there may be some artifacts from copy/pasting"

is much more readable than

  • "Looks like there may be some artifacts from 👮y/🍝e"

Following @evilsoft's remark, I've opened #358 to make sure we define a CoC in the future.

@DrBoolean
Copy link
Author

Ahh, haha okay I see now. I feel like that was an innocent move and I really value @evilsoft's help. Maybe we can disable the plugin and agree not to edit? I do find it fun despite being cryptic - i'll have to use that in my work slack :)

@KtorZ
Copy link
Member

KtorZ commented Dec 31, 2017

I do agree that the move may appear a bit like a anti-emojis-nazi kind of move, and was really not the intention in a first place. I really don't mind emojis when they aren't harmful to readability. Point taken though @evilsoft, I guess we just need to have that stated in a CoC.

@KtorZ KtorZ deleted the pt3 branch January 3, 2018 18:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

9 participants