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

Add more functions and tests to Js.Option #1669

Merged
merged 2 commits into from
Jun 2, 2017

Conversation

alcarvalho
Copy link
Contributor

I have added what I consider basic functions that should be in every standard library for options. I also created tests for all functions as we didn't have any for these.

I tried running the doc generating script here, but it changes a lot more files than what I was expecting, so I'm leaving this out for now. Let me know how to proceed in this case.

let map f x =
match x with
| None -> None
| Some x -> Some (f x [@bs])
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 the same as andThen

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Actually andThen is a flatMap. In that case f returns an option in this case f returns just a 'b.

Copy link
Contributor

@OvermindDL1 OvermindDL1 Jun 1, 2017

Choose a reason for hiding this comment

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

map is indeed correct here.

andThen is, to be honest, a wtf. Usually I know that function as bind, never seen it named andThen before and that name does not make sense to me... bind though is not much more descriptive really, but bind is the common name for it. flat_map I would like instead of andThen (what is with the camelcase anyway, ocaml is generally snake-case?).

Copy link
Contributor

Choose a reason for hiding this comment

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

andThen is used by Elm and Rust, at the very least. And bind is rightly discarded by any new language seeking some actual adoption. I'd be fine with flatMap (and snake_case too) but andThen (and camelCase) is more intuitive to a lot more developers.

Copy link
Contributor

Choose a reason for hiding this comment

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

Heh, I don't really get how it is andThening something, but flat_map makes sense based on the words. :-)

A use-case for and_then would be something that is 'temporal', like on a promise or so, but not on an option.

Copy link
Contributor

@OvermindDL1 OvermindDL1 Jun 1, 2017

Choose a reason for hiding this comment

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

Or it seems to work just as fine with flat_map while making more grammatical sense since you are not anding anything:

toValidMonth : Int -> Maybe Int
toValidMonth month =
    if month >= 1 && month <= 12 then
        Just month
    else
        Nothing

getFirstMonth : List Int -> Maybe Int
getFirstMonth months =
    head months
    |> Option.flat_map toValidMonth

Copy link
Contributor

Choose a reason for hiding this comment

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

Plus this would make a good usecase for filter too! ^.^

let is_valid_month month =
  month >= 1 && month <= 12

let to_valid_month month =
  month |> Option.filter is_valid_month

let head = function
  | [] -> None
  | first :: _ -> Some first

let get_first_month months =
  head months
  |> to_valid_month

So now to_valid_month is not doing the dual-task of both testing a predicate and packing a value (thus the predicate can be used elsewhere now), and piping into functions straight is always better when possible.

This is a side note though, but still, flat_map is still more descriptive than and_then, which just makes no grammatical sense, plus is an actual standard word. ;-)

Could just do both though. :-)

Copy link
Contributor Author

@alcarvalho alcarvalho Jun 1, 2017

Choose a reason for hiding this comment

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

Could just do both though. :-)

I was going to say that... maybe we should implement both the standard library signatures and have aliases that are friendlier to people coming from javascript?

Copy link
Contributor

@glennsl glennsl Jun 1, 2017

Choose a reason for hiding this comment

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

@OvermindDL1 I think you're missing the point of a somewhat contrived example :) However deliberate, it shouldn't be too hard to imagine other use cases that fit the pattern of do this thing (that returns an option) andThen do this other thing (that also returns an option) with it. It's certainly a more imperative viewpoint, but most devs do actually come from the imperative world. flatMap on the other hand is incomprehensible to anyone who hasn't already memorized its function, although it does have the nice property of being fairly easy to remember once you do learn of it.

Copy link
Contributor

@glennsl glennsl Jun 1, 2017

Choose a reason for hiding this comment

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

re: implementing both, I'd rather just have flatMap then. Having to memorize two functions is not in any way friendlier to newcomers than having to memorize just one. It's a non-solution that ignores the goal entirely.

Whether or not optimizing for friendliness should be a goal is of course debatable, but this is probably not the right place to have that discussion.

let orValue a x =
match x with
| None -> a
| Some x -> x
Copy link
Member

Choose a reason for hiding this comment

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

name to getWithDefault?

Copy link
Contributor

Choose a reason for hiding this comment

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

The stock OCaml Option module (bucklescript really needs to update) uses default for this exact same function:

let default d = function
| None -> d
| Some a -> a

Probably be good to add in the same names at the very least.

Copy link
Contributor

Choose a reason for hiding this comment

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

We should probably copy all of these: http://ocaml-lib.sourceforge.net/doc/Option.html

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@OvermindDL1 I thought I'd do that, but they said on Discord that this naming was intended to make sense to javascript developers. I questioned on whether we should instead just use the standard functions that you posted as that would make the code more reusable between server and client applications.

Copy link
Contributor

Choose a reason for hiding this comment

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

@OvermindDL1 I thought I'd do that, but they said on Discord that this naming was intended to make sense to javascript developers. I questioned on whether we should instead just use the standard functions that you posted as that would make the code more reusable between server and client applications.

Considering I'm planning to have bucklescript-tea have a server-side part soon it better work in native code as well. Even from what little of the horror's I've seen of the 'JS perspective' so far I still do not see how some of their names make any sense, like andThen for a concept as such as in here... >.>


let filter f x =
match x with
| None -> None
Copy link
Member

Choose a reason for hiding this comment

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

is this function common?

Copy link
Contributor

Choose a reason for hiding this comment

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

Eh, not really 'common', but it is something I've used before myself, would be nice to include it even if it is not really 'standard'.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I use this all the time in Scala and Swift, to further narrow options. Usually filtering empty strings, for instance. I've seen it in every stdlib of languages that support options.

Copy link
Contributor

Choose a reason for hiding this comment

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

I've seen it in every stdlib of languages that support options.

Yet not in the OCaml Option module. ^.^

But still, I'd like it in, it is a pattern I use. :-)

Copy link
Member

Choose a reason for hiding this comment

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

cool, lets add filter then.
@OvermindDL1 there is no option module in ocaml stdlib so far as I know..

@bobzhang
Copy link
Member

bobzhang commented Jun 2, 2017

We can argue about naming forever, let's stick to camlCase : ). The reason is existing JS api is camlCase, if we use snake_case, then we will introduce two sets of name conventions

@OvermindDL1
Copy link
Contributor

We can argue about naming forever, let's stick to camlCase : ). The reason is existing JS api is camlCase, if we use snake_case, then we will introduce two sets of name conventions

OCaml already has a convention though. ;-)

Let Reason libraries do things JS'y, OCaml should stay OCaml.

@bobzhang
Copy link
Member

bobzhang commented Jun 2, 2017 via email

@OvermindDL1
Copy link
Contributor

In libraries not really I've noticed as well, but in the standard library it is fairly consistent (at least I cannot think of any camelCase off-hand, much as that would go with the OCaml name. ^.^).

@alcarvalho
Copy link
Contributor Author

Ok, so let's try to summarize the changes here? :)

  • Change orValue function to default so it's the same as in the stdlib.
  • Keep everything else as is? So not don't add aliases to functions.

@OvermindDL1
Copy link
Contributor

OvermindDL1 commented Jun 2, 2017

Change orValue function to default so it's the same as in the stdlib.

The Core lib uses value though, it might be the best style to use as the upcoming stdlib changes are modeled heavily on Core. Specifically it defines in Option:

val value : 'a t -> default:'a -> 'a

I really do not like that the named argument is last instead of first, it makes piping it much more difficult...
It also uses these for some of the above as well:

val value_map : 'a t -> default:'b -> f:('a -> 'b) -> 'b
val map2 : 'a t -> 'b t -> f:('a -> 'b -> 'c) -> 'c t
val call : 'a -> f:('a -> unit) t -> unit
val apply : 'a -> f:('a -> 'b) t -> 'b t

Among others: https://ocaml.janestreet.com/ocaml-core/109.12.00/doc/core/Option.html

I could see javascript users liking these names a lot better than the more 'traditional' names as well.

Maybe it would be best to subsume's Core.Option instead? (Maybe other Core modules too?) (Perhaps less labels too, or supply both?)

@alcarvalho
Copy link
Contributor Author

@OvermindDL1 I think we should follow the stdlib here and if you want to use Core, then you'll be able to, now that they've released the Base library. It should be compatible with bucklescript.

@bobzhang
Copy link
Member

bobzhang commented Jun 2, 2017

@alcarvalho would you sign off your commit (see CONTRIBUTION.md) and I will merge it thanks!
@OvermindDL1 contributions are welcome : )

Andre Carvalho added 2 commits June 2, 2017 14:28
Signed-Off-By: Andre L. Carvalho <me@andrecarvalho.org>
Signed-Off-By: Andre L. Carvalho <me@andrecarvalho.org>
@alcarvalho alcarvalho force-pushed the optional-standard-funs branch from f71dcdd to e321cfe Compare June 2, 2017 18:29
@alcarvalho
Copy link
Contributor Author

@bobzhang done! Thanks!

@bobzhang bobzhang merged commit 28ea75e into rescript-lang:master Jun 2, 2017
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.

4 participants