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 (?) to Maybe? #216

Closed
evancz opened this Issue Apr 15, 2015 · 15 comments

Comments

Projects
None yet
9 participants
@evancz
Member

evancz commented Apr 15, 2015

With lots of core functions becoming total, it makes sense to consider an infix version of withDefault.

(?) : Maybe a -> a -> a

firstNumber =
    head numberList ? 0

If you want to get the head of a list of numbers or just go with zero if it is empty. This is really cool, but here are two main concerns:

  1. Are we adding too many infix operators?
  2. What precedence this operator should have?

Can you share some examples that make the case for adding this function? Do you have any examples that help address concerns 1 or 2?

@TheSeamau5

This comment has been minimized.

Show comment
Hide comment
@TheSeamau5

TheSeamau5 Apr 19, 2015

Contributor

I'll try to help give context for 1) by listing all the non-mathy/non-logic-y infix operators currently in Elm:

  • :: (cons)
  • |> (apply right)
  • <| (apply left)
  • >> (compose right)
  • << (compose left)
  • <~ (Signal.map)
  • ~ (Signal.mapN)
  • ++ (List.append or String.append)
  • := (Json.Decode.decodeField)
Contributor

TheSeamau5 commented Apr 19, 2015

I'll try to help give context for 1) by listing all the non-mathy/non-logic-y infix operators currently in Elm:

  • :: (cons)
  • |> (apply right)
  • <| (apply left)
  • >> (compose right)
  • << (compose left)
  • <~ (Signal.map)
  • ~ (Signal.mapN)
  • ++ (List.append or String.append)
  • := (Json.Decode.decodeField)
@evancz

This comment has been minimized.

Show comment
Hide comment
@evancz

evancz Apr 19, 2015

Member

I just saw that something similar seems to exists in C#. What other languages have something like this?

Member

evancz commented Apr 19, 2015

I just saw that something similar seems to exists in C#. What other languages have something like this?

@kasbah

This comment has been minimized.

Show comment
Hide comment
@kasbah

kasbah Apr 19, 2015

Contributor

Coffeescript (see #Existence:)

Contributor

kasbah commented Apr 19, 2015

Coffeescript (see #Existence:)

@TheSeamau5

This comment has been minimized.

Show comment
Hide comment
@TheSeamau5

TheSeamau5 Apr 19, 2015

Contributor

There's a long list here: https://en.wikipedia.org/wiki/Null_coalescing_operator

Apparently it's called the "null coalescing operator".

They don't all agree on the exact syntax. But here's a list of the versions I got from just skimming through the wiki page:

  • ?
  • ??
  • :?
  • ?:
  • //

Usually, there's an avoidance of the first one I'm guessing because of the existence of the ternary operator x == null ? x : 0 in those languages. But Elm doesn't have the ternary operator, so it can just pick whatever. :D

Contributor

TheSeamau5 commented Apr 19, 2015

There's a long list here: https://en.wikipedia.org/wiki/Null_coalescing_operator

Apparently it's called the "null coalescing operator".

They don't all agree on the exact syntax. But here's a list of the versions I got from just skimming through the wiki page:

  • ?
  • ??
  • :?
  • ?:
  • //

Usually, there's an avoidance of the first one I'm guessing because of the existence of the ternary operator x == null ? x : 0 in those languages. But Elm doesn't have the ternary operator, so it can just pick whatever. :D

@kasbah

This comment has been minimized.

Show comment
Hide comment
@kasbah

kasbah Apr 19, 2015

Contributor

Maybe it could be:

(?) : Maybe a -> Bool

(?:) : Maybe a -> a -> a

which would looke like this in use:

if (head xs)? then f xs else g 

a = head xs ?: x
Contributor

kasbah commented Apr 19, 2015

Maybe it could be:

(?) : Maybe a -> Bool

(?:) : Maybe a -> a -> a

which would looke like this in use:

if (head xs)? then f xs else g 

a = head xs ?: x
@VulumeCode

This comment has been minimized.

Show comment
Hide comment
@VulumeCode

VulumeCode Apr 21, 2015

To address point 2, if it's supposed to chain you should take that into account:

head numberList1 ? head numberlList2 ? 0

And you could consider it for record access:

task : Maybe Task
task?.description : Maybe String

Works nicely together:

task?.description ? "" : String

VulumeCode commented Apr 21, 2015

To address point 2, if it's supposed to chain you should take that into account:

head numberList1 ? head numberlList2 ? 0

And you could consider it for record access:

task : Maybe Task
task?.description : Maybe String

Works nicely together:

task?.description ? "" : String
@mitchmindtree

This comment has been minimized.

Show comment
Hide comment
@mitchmindtree

mitchmindtree Apr 23, 2015

In reply to both this and #215, I just thought I'd share Rust's take on Maybe (called Option).

  • .unwrap() forces the value from the Option (and fails if it is None (aka Nothing)).
  • .expect("Message explaining what was expected.") does the same as .unwrap(), however it also provides an error message in the case that the value was None and failed (this is usually preferred to unwrap as it lets the user provide clarification for the error, though both methods are normally avoided if possible).
  • .unwrap_or(default) does the same as .unwrap(), but rather than failing on None it provides the default value (same as Elm's withDefault).

There's a bunch of other handy methods here too if you're interested (I'm not particularly advocating any of these, just thought I'd share).

In my opinion the (?) operator is clever, though perhaps a little less clear than withDefault and maybe not common enough to justify it's own operator?

mitchmindtree commented Apr 23, 2015

In reply to both this and #215, I just thought I'd share Rust's take on Maybe (called Option).

  • .unwrap() forces the value from the Option (and fails if it is None (aka Nothing)).
  • .expect("Message explaining what was expected.") does the same as .unwrap(), however it also provides an error message in the case that the value was None and failed (this is usually preferred to unwrap as it lets the user provide clarification for the error, though both methods are normally avoided if possible).
  • .unwrap_or(default) does the same as .unwrap(), but rather than failing on None it provides the default value (same as Elm's withDefault).

There's a bunch of other handy methods here too if you're interested (I'm not particularly advocating any of these, just thought I'd share).

In my opinion the (?) operator is clever, though perhaps a little less clear than withDefault and maybe not common enough to justify it's own operator?

@kosiakk

This comment has been minimized.

Show comment
Hide comment
@kosiakk

kosiakk Jun 21, 2016

Kotlin language for JVM/JS has a smart way of dealing with nulls, probably inspired by Groovy:

  • Elvis operator ?: is a visual shortcut for ternary operator, "if null then this"
  • Save dot operator ?. which returns null itself if applied to null
  • Unsafe take operator from #215 is !!. Unsafe dot !!. is just a combination of the two infix operators, not a special operator.

Originally they've named such unsafe function sure(), like "I'm sure there will be value", but exclamation marks grabs attention much better than any text. And it's an antonym to question mark operators.

Question mark alone sounds like "if", but we are looking for "if not, then", "orElse".

According to the Kotlin grammar:

  • ?. and !! have the same semantics and highest precedence as . itself.
  • Elvis operator occupies dedicated precedence level between default infix operators and comparison.

kosiakk commented Jun 21, 2016

Kotlin language for JVM/JS has a smart way of dealing with nulls, probably inspired by Groovy:

  • Elvis operator ?: is a visual shortcut for ternary operator, "if null then this"
  • Save dot operator ?. which returns null itself if applied to null
  • Unsafe take operator from #215 is !!. Unsafe dot !!. is just a combination of the two infix operators, not a special operator.

Originally they've named such unsafe function sure(), like "I'm sure there will be value", but exclamation marks grabs attention much better than any text. And it's an antonym to question mark operators.

Question mark alone sounds like "if", but we are looking for "if not, then", "orElse".

According to the Kotlin grammar:

  • ?. and !! have the same semantics and highest precedence as . itself.
  • Elvis operator occupies dedicated precedence level between default infix operators and comparison.

@elm elm locked and limited conversation to collaborators Jun 26, 2016

@elm elm unlocked this conversation Jun 26, 2016

@yuri-martynov

This comment has been minimized.

Show comment
Hide comment
@yuri-martynov

yuri-martynov Jul 8, 2016

I would like to have (?) for Bool as well
C#, F# has
test ? if_true : If_false

as a shortcut to

if test then
if_true
else
if_false

yuri-martynov commented Jul 8, 2016

I would like to have (?) for Bool as well
C#, F# has
test ? if_true : If_false

as a shortcut to

if test then
if_true
else
if_false

@kosiakk

This comment has been minimized.

Show comment
Hide comment
@kosiakk

kosiakk Jul 8, 2016

In Elm if is an expression, i.e. it returns a value. Therefore there is no ternary operator condition ? then : else, because ordinary if works fine in this role:

answer = if powerLevel > 9000 then "OVER 9000!!!" else "meh"

I'd say it is much easier to understand for beginner programmers.
Also, the idiomatic way of dealing with such conditions and, especially, control flow is different from C.

kosiakk commented Jul 8, 2016

In Elm if is an expression, i.e. it returns a value. Therefore there is no ternary operator condition ? then : else, because ordinary if works fine in this role:

answer = if powerLevel > 9000 then "OVER 9000!!!" else "meh"

I'd say it is much easier to understand for beginner programmers.
Also, the idiomatic way of dealing with such conditions and, especially, control flow is different from C.

@jvoigtlaender

This comment has been minimized.

Show comment
Hide comment
@jvoigtlaender

jvoigtlaender Jul 8, 2016

Contributor

May I suggest to simply close this issue? Infix operators have lost popularity around here, so if no compelling need was felt to nevertheless add this ? operator over the course of the last 15 months the issue has been open, then probably this is not going to change.

Also, closing the issue will curb desires to have the operator available in even more situations. 😄

Contributor

jvoigtlaender commented Jul 8, 2016

May I suggest to simply close this issue? Infix operators have lost popularity around here, so if no compelling need was felt to nevertheless add this ? operator over the course of the last 15 months the issue has been open, then probably this is not going to change.

Also, closing the issue will curb desires to have the operator available in even more situations. 😄

@yuri-martynov

This comment has been minimized.

Show comment
Hide comment
@yuri-martynov

yuri-martynov Jul 8, 2016

elm-format (following style guide) breaks one line
answer = if powerLevel > 9000 then "OVER 9000!!!" else "meh"
to six. It makes me unhappy as a beginner Elm programmer.

And I am OK with
x |> Maybe.withDefault 0

I find that control flow with Maybe is more "complex" than control flow with Bool and needs to be handled with pattern matching. Substituting Maybe with a default value looks like a hack for most of the cases for me.

But I found that infix operator with lazy evaluation will be helpful

`(??) : Maybe a -> (()->a) -> a'

or syntax sugar for

result = tryCalculateFirst ?? tryCalculateSecond ?? failWithDefaultValue

yuri-martynov commented Jul 8, 2016

elm-format (following style guide) breaks one line
answer = if powerLevel > 9000 then "OVER 9000!!!" else "meh"
to six. It makes me unhappy as a beginner Elm programmer.

And I am OK with
x |> Maybe.withDefault 0

I find that control flow with Maybe is more "complex" than control flow with Bool and needs to be handled with pattern matching. Substituting Maybe with a default value looks like a hack for most of the cases for me.

But I found that infix operator with lazy evaluation will be helpful

`(??) : Maybe a -> (()->a) -> a'

or syntax sugar for

result = tryCalculateFirst ?? tryCalculateSecond ?? failWithDefaultValue

@jvoigtlaender

This comment has been minimized.

Show comment
Hide comment
@jvoigtlaender

jvoigtlaender Jul 8, 2016

Contributor

@yuri-martynov, if I understand correctly, you are proposing two things now:

  1. A separate feature: ? as an operator version of if-then-else. Note that this is not something that can be done in core. Instead, it will have to be wired into the compiler (to get the short-circuiting behavior). So it seems something that you should bring up elsewhere, not here in the library repo.
  2. An alternative to Maybe.withDefault and (?) : Maybe a -> a -> a with different type and slightly different semantics.
Contributor

jvoigtlaender commented Jul 8, 2016

@yuri-martynov, if I understand correctly, you are proposing two things now:

  1. A separate feature: ? as an operator version of if-then-else. Note that this is not something that can be done in core. Instead, it will have to be wired into the compiler (to get the short-circuiting behavior). So it seems something that you should bring up elsewhere, not here in the library repo.
  2. An alternative to Maybe.withDefault and (?) : Maybe a -> a -> a with different type and slightly different semantics.
@7sharp9

This comment has been minimized.

Show comment
Hide comment
@7sharp9

7sharp9 Jul 8, 2016

Note: F# does not have the ternary operator ? like C#. If / else expressions are written the same as Elm.

7sharp9 commented Jul 8, 2016

Note: F# does not have the ternary operator ? like C#. If / else expressions are written the same as Elm.

@evancz

This comment has been minimized.

Show comment
Hide comment
@evancz

evancz Jul 9, 2016

Member

I don't think it makes sense to go with this now that a bunch of things have been returning Maybe for quite some time now, and the following things are true:

  1. Maybe.withDefault works nicely with (|>) and has an explicit name.
  2. Writing code that way is nicer to read, especially for beginners, and encourages better style in teams.
  3. Outside of (<|) and (|>) I have never added an infix operator that was actually a good idea in the end.
Member

evancz commented Jul 9, 2016

I don't think it makes sense to go with this now that a bunch of things have been returning Maybe for quite some time now, and the following things are true:

  1. Maybe.withDefault works nicely with (|>) and has an explicit name.
  2. Writing code that way is nicer to read, especially for beginners, and encourages better style in teams.
  3. Outside of (<|) and (|>) I have never added an infix operator that was actually a good idea in the end.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment