DEV Community

Discussion on: Monad Say What? (Part 1)

 
antonrich profile image
Anton • Edited

To be honest, I haven't.

The reason is... actually, it doesn't matter what the reason is.

I will do what I'm supposed to do.

I will finish what I started.


Now I will write my comment again in the same style. I have attached my gedit window with the above all other windows option. I will reread the article especially the functional part (because some of the functions I don't understand). I will narrate as I go along. I will call this "narration style".


const { isArray, isObject, isString } = require('crocks')

I have to say that I'm not that proficient in JS. So correct me if I'm wrong. What I think I see here is destructuring. Pattern matching in other words. But on the right side there is a library so I don't really see how that works. I think I'll go right now and watch something on youtube to understand that part. I'll be as thorough as possible.


I opened youtube and searched es6 destructuring. I see a lot of videos. I'm going with the fun fun function video. If necessary I will narrate my process.

Here's an example:

let animal =
{ species: 'dog'
, weight: 23
, sound: 'woof'
}

// the destructuring
let { species, sound } = animal

// and from this I understand that the order doesn't matter.

Well, that's actually all I need at the moment. If order doesn't matter I can take out any function out of the library.

That's it. The video is 10 minutes long, I stopped at 1:27. Back to the tutorial.


At this moment I'm reading the requirements to really get them.

When the data is not a non-empty Array of at least one acceptable record, an empty Object will be returned.

This part, was a bit hard to understand. Because of the use of the "double negative". I understand that it's not. But you can simplify it.

I'd go with:

When the data is an Array with at least one acceptable record, an empty Object will be returned.

Tell me if I've "refactored" your requirement correctly.


Oh, I'm looking the type signature:

// data :: [ * ]

the "*" is not a type variable. But a wildcard. It kinda expects something unpredictable inside an array.

You know I also look at this:

From these requirements, we can implement an imperative function that does the following:

Verify input is an Array, return an empty Object if it isn't
Declare a result accumulator for building our final result, defaulting it to an empty Object.
Iterate over the provided Array and do the following for each item:
    Validate the item against our record criteria
    If passed, add the record to the result, keyed by the id value on the record. Otherwise do nothing.
Return the result.

Using a third-party library called crocks for some doing our type validation, we can provide an implementation like this:

And think that this could be written as comments in the code.

And that would be may be better, or not. Is there a way to find out?

At this point, I remembered that your purpose was to talk about ADT and monad. I went to the beginning of the article.

I'm not familiar with the word embelishment. I'm looking it up.

Embelishment is a decorative detail (elaboration, or addition).

I opened my editor to retype the example. I'm going back and forth between retyping and reading

"From these requirements, we can implement an imperative function that does the following:"

I do it slowly so I understand everything thoroughly. I'm going back and forth. I read one implementation, then retype and think. Then I read another one, and then retype again.

Oh, now I've noticed you don't have semicolons in the code. That's good.

I need to download the 'crocks' library. To run the examples. I`ve just installed crocks.
npm install crocks -S

It says "All you need to do is run the following to save it as a dependency in your current project folder".

How do I save it as a dependancy? What is -S by the way?


Okay, I've got the imperative code well, I think. Now, to the functional part. Now, I think I should the functional part in a separate file. Or not?
Gosh, it's 1:43 in the morning. I'm getting so sleepy. I will continue tomorrow when I wake up.


Alright, I have woken up.
I want to make sure that I get the function composition in JS.

In Haskell we do it like this.

func1 . func2 . func3 x x

So in JS that would be

composeK(func1(Int), composeK(func2(Int), func3(Int, Int)))

This is hypothetical, but I think I get it right.

What I see that I don't understand is Maybe.of. After that I looked at the converge function. It takes a lot of functions as arguments. Right now I'm looking at the documentation on converge. The reason I'm diving deep is because I want to do the exercises.

Now I see this. I doesn't take all the arguments, so to speak. So there is partial application at play. Maybe.of takes data as an argument and then it all reduces to one value.

At this point I haven't scroll down, but I remember you have given some explanations below. Now it's time to read them.

The function Safe led me to Maybe in the library. I'm scrolling and see chain there as well. I looked at both don't really understand them.

Let's continue anyway.

So, I see the reference to the fluent api. Which doesn't tell me anything, I tried understand it, but decided that is just too much.

Conclusion: the reference to the fluent api is completely unnecessary and will create more confusion.


Now I will go through the exercises.

  1. My immediate response is why would I use reduce when I just need to filter. I have problems with Haskell's fold function. So, I have the same problem with the reduce function.

If the reduce works with numbers and these numbers will be reduced to a singe value I understand.

But when the reduce (or fold in my practice) works with different data types other than numbers I have problems.

Okay, I just thought it about.

Here we have an empty array as an accumulator, and we will push object that are satisfy a predicate.
Now should I write this predicate as a helper function?

Here's my little refactor:

`js
let result = records.reduce(filter_records(records), [])

const filter_records = records => {
return isObject(records) && isString(records.id)
}
`
I think it's not correct.

Will this work?

`js

let result = records.reduce(filter_records(sum, records), [])

const filter_records = (sum, records) => {
return sum + (isObject(records) && isString(records.id))
}
`

I unfortunately, cannot find out at the moment. Because, I have no idea of how to connect Crocks. It is installed, I've never used package.json, I've not used any build tools yet.
Here, I need some help.

2 - 5. I feel that lack of knowledge of JS is a little hindrance and that every time I do something I need to dig deeper. I wouldn't say I'm lazy. But in this particular instance I am. Maybe I will return at some point and do the exercises. I liked the "reduce" exercise.


Why did I wrote all those comments?

  • Some people say that when you get the monand you lose the ability to explain it. I want to reflect and see if I get the monad and if it's really true.
  • So, I started and I need to finish. I want to be a finisher. I want to be a man of action and a man of my word. Being a man of my word is really hard for me. I do tend to commit to a lot of things but accomplish less. So it's kind of personal growth for me.
  • Your feedback is also valuable for me.
  • I have a slight interest in functional JS (But who knows how it's gonna work out in the future). But I hope that JS will be as functional as possible. That is my little contribution to that hope.
  • Another reason is. I may have acquired a bad behavioral pattern with the read later button. I once have read on Scott H Young`s blog that it's better to learn something deeply right away when you approach the problem rather than putting it off for later. This is my attempt to do exactly that.
Thread Thread
 
evilsoft profile image
Ian Hofmann-Hicks • Edited

So good.

So there are a couple things you've shown me I need to do. First of I need to update:

These posts assume an understanding of "currying", "partial application" and "function composition". If you feel a bit fuzzy on these topics, there are many resources available on the webs to get you sorted out.

To read more like:

These posts assume an understanding of not only the JavaScript language, but how "currying", "partial application" and "function composition" is accomplished in Javascript....

Also good call on the needing of crocks for the POJ example. I need to break those out into functions like:

// isArray :: a -> Boolean
const isArray =
  Array.isArray

// isString :: a -> Boolean
const isString = x =>
  typeof x === 'string'

// isObject :: a -> Boolean
const isObject = x =>
  !!x && Object.prototype.toString.call(x) === '[object Object]'

Yeah people should not have to install crocks at this stage in the game.

When the data is an Array with at least one acceptable record, an empty Object will be returned.

How about

Unless the data is an Array with at least one acceptable record, an empty Object will be returned.

Arrays in JS can be of mixed type, so we typically denote that with [ * ], as opposed to something like [ a ] where we expect everything to be that a. Well when our functions can work with mixed typed Arrays. In reality we want to strive for [ a ] or even better something like [ Number ], but sometimes at the edge, we need the [ * ]

You got mostly the compose bits down, except you would just use compose, composeK is for Kleisli arrows ( basically >=> or fish in Haskell). Also you can use either compose or composeK like this:

// x => fn1(fn2(fn3(x)))
const fn =
  compose(fn1, fn2, fn3)

My immediate response is why would I use reduce when I just need to filter.

So this is more than just a filter, we are change types. we move from an Array to an Object. So we want to reduce with {} as our empty on the fold, then only add valid records keyed by their valid id to the accumulator. Does that make sense?

Thread Thread
 
evilsoft profile image
Ian Hofmann-Hicks • Edited

Also just to help a fellow Haskeller out here is a quick JS -> Haskell key:

  • compose -> . (but compose is n-Ary)
  • composeK -> >=> (but composeK is n-Ary)
  • [type].of -> pure for Applicative Functor / return for Monad
  • [instance].chain -> bind
  • liftA2 -> liftA2
  • converge -> S' or Phoenix Combinator.
Thread Thread
 
antonrich profile image
Anton

I mostly don't know about them. So you are telling me new stuff that I should learn : ))) That's great. Thanks.