sanctuary-js / sanctuary

:see_no_evil: Refuge from unsafe JavaScript
https://sanctuary.js.org
MIT License
3.03k stars 94 forks source link

write "getting started" guide #210

Open davidchambers opened 8 years ago

davidchambers commented 8 years ago

As suggested by @riston on Gitter.

thurt commented 8 years ago

After reading the summary/introduction, the full picture of where Sanctuary extends beyond Ramda is not entirely clear. Does it add new functions or just replace existing Ramda functions with type safe versions?

davidchambers commented 8 years ago

Earlier today @dypsilon described Sanctuary as "a Ramda reboot". I had not thought of it this way, but it's an accurate description.

The two libraries have a great deal in common philosophically. Points of agreement:

The two libraries differ philosophically in a few significant ways. Points of disagreement:

Let's consider the points of difference. Ramda provides some variadic functions (such as R.pipe); Sanctuary provides none. Many of Ramda's higher-order functions accept variadic functions. In my view—a view shared by @Avaq and @benperez—this increases implementation complexity, documentation complexity, and usage complexity. I rarely succeed in getting R.lift to behave as I expect, yet S.lift2 is one of my favourite functions. I can look at its type signature and see how to use it:

S.lift2 :: Apply f => (a -> b -> c) -> f a -> f b -> f c

With R.lift on the other hand, I'm forced to read its description as I don't know what to make of this:

R.lift :: (*... -> *) -> ([*]... -> [*])

Ramda has many partial functions, such as R.head. Sanctuary is able to provide total equivalents as it has access to Maybe and Either which encapsulate the notion of success and failure. Not only do these types allow Sanctuary to provide safe versions of unsafe Ramda functions, but these types are available for one to use in one's own code.

Ramda and Sanctuary disagree on whether add('foo', 'bar') should evaluate to 'foobar' or raise an exception. Ramda's approach could be described as "let it fail (or succeed with an erroneous value)", whereas Sanctuary goes to great lengths to prevent type errors from going undetected.

On this last point, @buzzdecafe has said that he believes run-time type checking would benefit Ramda. It's possible Ramda and Sanctuary will reconcile this difference. :)

dypsilon commented 8 years ago

I was primarily talking about Ramda and Sanctuary in the context of working with types. I think Ramda does a great job of working with native data types. You could just drop Ramda in any project and have a great time working with those arrays and strings in a somewhat functional way.

On the other hand, Ramda is awkward for working with algebraic types as defined in Fantasy Land. This is the reason why there are so many libraries which redefine simple functions, already included in Ramda, for working with algebraic types.

My suggestion was to stop complicating Ramda with algebraic type classes and leave it to Sanctuary. Especially because the former doesn't have any type checking on board.

buzzdecafe commented 8 years ago

@davidchambers i am coming around. my major problems with run-time type-checking are

  1. i feel like it misses the point somehow; that you are missing the benefits of typing by waiting until run-time to try and enforce; and
  2. more importantly, the problem of enforcing types at run-time is not personally interesting to me

But the benefits are clear, and I don't dispute that something is better than nothing.

davidchambers commented 8 years ago

Ramda is awkward for working with algebraic types as defined in Fantasy Land.

I don't see it this way. I quite happily use R.map and R.chain with algebraic data types provided by Fluture, Folktale, and Sanctuary. I appreciate Ramda providing these functions irrespective of whether Ramda provides any algebraic data types.

My suggestion was to stop complicating Ramda with algebraic type classes and leave it to Sanctuary.

I consider transducers the most significant source of complexity in Ramda's implementation. Having R.map, R.chain, etc. dispatch doesn't strike me as problematic, though providing usage examples for certain functions—R.sequence comes to mind—is challenging if one restricts oneself to built-in types.

buzzdecafe commented 8 years ago

i see the placeholder as the biggest offender. I agree that transducers add complexity, but they also pay off. The placeholder adds overhead to the most fundamental operation in the libarry and delivers nothing that you can't get with flip.

davidchambers commented 8 years ago

I agree that transducers add complexity, but they also pay off.

I agree. Cost–benefit analysis is important. I believe the benefit of R.map, R.chain, etc. dispatching significantly outweighs the cost of doing so.

buzzdecafe commented 8 years ago

I believe the benefit of R.map, R.chain, etc. dispatching significantly outweighs the cost of doing so.

Yes, me too. The intent was to provide a point-free interface for FL types and look more ML-ish. I'm curious exactly what @dypsilon finds awkward?

CrossEye commented 8 years ago

Perhaps it's time for Ramda to consider how to pull Transducers into a side package such as Fantasy and Lenses.

The placeholder adds overhead to the most fundamental operation in the library and delivers nothing that you can't get with flip.

I'm not sure I go as far as that. There all sorts of things that could be done with, say, reduceBy and the placeholder that cannot be done with flip. Nonetheless, I would love to find a clean way to get rid of the placeholder and keep those abilities. I no longer find it very convincing.

thurt commented 8 years ago

Here is a very simple comparison between Ramda/Sanctuary function names. It is at least helpful for me to visualize some of the differences b/w the two libraries, so thought I would share: demo: http://rawgit.com/thurt/ramda-sanctuary-compare/master/index.html repo: https://github.com/thurt/ramda-sanctuary-compare

davidchambers commented 8 years ago

That's a useful document, @thurt! Keep in mind that the shared functions are in same cases equivalent when used correctly (e.g. trim) but are in other cases significantly different (e.g. head). Regardless, this document provides a good indication of the overlap between the two libraries.

thurt commented 8 years ago

True--comparing just by function name is like comparing apples-to-oranges in some cases. It looks like the either function is one example. http://ramdajs.com/0.21.0/docs/#either https://github.com/sanctuary-js/sanctuary#either--a-c---b-c---eitherab---c

futpib commented 6 years ago

I would appreciate an example definition of a custom type class.

Also #390 looks like a duplicate (or at least related).

davidchambers commented 6 years ago

I would appreciate an example definition of a custom type class.

Are the definitions of Foo :: TypeClass and Bar :: TypeClass in the Z.TypeClass documentation helpful? If not, how do you suggest we improve the examples?

futpib commented 6 years ago

@davidchambers This is certainly helpful.

In my opinion a perfect example would include:

  1. TypeRep definition
  2. TypeClass definition
  3. One instance method
  4. One type representative ("static") method
  5. A conventional way to put it together in npm module (how you export all the things)
davidchambers commented 6 years ago

That's a good outline, @futpib. Would you like to take the lead on this? I'm happy to provide feedback.

If you're interested, you could open a pull request to this repository containing a single Markdown file, we could get feedback from the community and make changes, then copy the final version and paste it into the wiki.

Fl4m3Ph03n1x commented 6 years ago

Hey, are there any news to this ? Has an getting started been written yet? Would really love some documentation!

davidchambers commented 6 years ago

There's no update yet, @Fl4m3Ph03n1x. Sorry.

I ran a two-hour workshop at LambdaConf last month. It was recorded, and the recording will at some point be uploaded to YouTube. I'll share a link here once that has happened.

Would really love some documentation!

What would you like to know? Why one might use Sanctuary, or how to install and configure it, or how to use it, or some combination of these, or something else entirely?

Fl4m3Ph03n1x commented 6 years ago

Ah yes, the workshop would be great! As a beginner into Sanctuary and after reading some docs, I am aware of the following ( what I consider strong points of the documentations ):

  1. Good comparison of Ramda VS Sanctuary
  2. Sanctuary's goal and mission
  3. Sanctuary's architecture ( separated modules )

Now, I feel I am missing the following:

  1. Some examples of the API are ... welll... could be improved! As a case I mention the Configure section Identity['@@type'] = 'my-package/Identity@1';. Now I am sure this makes sense to you, but for me, I wonder why I need @@ for example.
  2. It is totally not clear to me, reading from the docs, how I would integrate it within a project and define functions with signatures and so on.
  3. Code sample to justify the long Maybe / Either APIs.

IMHO, code sample of small projects ( like reading from a file using Node.js fs or makign an HTTP request ) would be really useful. I see half the API as having a ton of methods, but for me, I really don't see how most them when could be applied in the real world ( where and why would I use pipeK instead of pipe ? ).

To this end, small demo projects ( which are usually presented at conferences ) are a great help, at least for me, and I really wish there were more.


As a side way, it would also be interesting to add a section of "relevant articles" that others made to help ease people into Sanctuary and to provide them with additional documentation.

davidchambers commented 6 years ago

Thank you for your thoughtful response, @Fl4m3Ph03n1x. I find it very helpful indeed.

As a case I mention the Configure section Identity['@@type'] = 'my-package/Identity@1';. Now I am sure this makes sense to you, but for me, I wonder why I need @@ for example.

@@type provides compatibility with sanctuary-type-identifiers. Could we update the documentation to make this apparent?

It is totally not clear to me, reading from the docs, how I would integrate it within a project and define functions with signatures and so on.

I sympathize. Sanctuary has many moving parts and it's not obvious how one should fit them together.

Code sample to justify the long Maybe / Either APIs.

I'm not sure what you mean by this. Do you mean that Sanctuary contains many functions for working with values of types Maybe a and Either a b, and that it's not obvious why one would use some of these functions? If so, are there functions which seem particularly unhelpful?

As a side way, it would also be interesting to add a section of "relevant articles" that others made to help ease people into Sanctuary and to provide them with additional documentation.

This is another excellent suggestion. :)

Fl4m3Ph03n1x commented 6 years ago

I'm not sure what you mean by this. Do you mean that Sanctuary contains many functions for working with values of types Maybe a and Either a b, and that it's not obvious why one would use some of these functions? If so, are there functions which seem particularly unhelpful?

What I mean by this is that Maybe has 18 methods, while Either has 16. Now, while I agree that isJust ( for example ) is self-explanatory, others like encase are not so ( why would I need it? eval is kinda evil :p ).

Compared to, for example, Folktale, which has about 2 methods for their Maybe and Either ( Result ) types (getOr and merge ) I personally find the API surface sanctuary offers quite staggering.

It is not to say that this is a bad thing, it's just that as a newcomer, I would be more inclined towards Folktale because I feel the learning curve would be smaller while retaining the same benefits Sanctuary offers.

davidchambers commented 6 years ago

This may be a clearer example for S.encase:

const { unsafeGetUser } = require ('some-external-dependency');

//    getUser :: Integer -> Maybe User
const getUser = S.encase (unsafeGetUser);

We've defined a pure function in terms of an impure function. :)

Your comments about the daunting API are well taken. The remedy, I believe, is not to remove functions but to provide tutorials so that new users are not forced to read what is essentially reference material.

gabejohnson commented 6 years ago

@davidchambers I would argue that rights and lefts could be combined into

separate :: (Filterable f, Functor f) => f (Either a b) -> Pair (f a) (f b)

or

separate :: (Filterable f, Functor f) => f (Either a b) -> { lefts :: f a, rights :: f b }

see https://github.com/LiamGoodacre/purescript-filterable/blob/0b9b0994704f29e75072e6b3e6b8658b93b35ab8/src/Data/Compactable.purs#L66

davidchambers commented 6 years ago

Please open an issue for that proposal, Gabe. :)

Fl4m3Ph03n1x commented 6 years ago

Is there a way I could help to contribute to this guide? Is there something in the works that perhaps requires feedback?

davidchambers commented 6 years ago

Is there a way I could help to contribute to this guide?

That would be wonderful! Would you like to contribute to the wiki? If so I will create a new wiki entry. To encourage participation from me and others, I suggest publishing something and requesting input. :)