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

Sugar: Some 10 |> function (Some x) -> x | ... #605

Closed
xp44mm opened this Issue Sep 10, 2017 · 10 comments

Comments

Projects
None yet
5 participants
@xp44mm

xp44mm commented Sep 10, 2017

sugar: a |> function (Some x) -> x | ...

I propose we ... (describe your suggestion here)

let a = Some 10
a |> function (Some x) -> x  | ...

replace | _ -> failwith "never" with new structure | ...
The existing way of approaching this problem in F# is ...

let a = Some 10
a |> function (Some x) -> x  | _ -> failwith "never"

a alternative sugar is four |:

function (Some x) -> x  ||||

Pros and Cons

The advantages of making this adjustment to F# are
Silence Speaks Better Than Sounds

The disadvantages of making this adjustment to F# are
new structure | ... will be introduced.

@xp44mm xp44mm changed the title from sugar: a |> function (Some x) -> x | ... to Sugar: Some 10 |> function (Some x) -> x | ... Sep 10, 2017

@xuanduc987

This comment has been minimized.

Show comment
Hide comment
@xuanduc987

xuanduc987 Sep 10, 2017

Or you can just use #nowarn "25"

xuanduc987 commented Sep 10, 2017

Or you can just use #nowarn "25"

@rmunn

This comment has been minimized.

Show comment
Hide comment
@rmunn

rmunn Sep 11, 2017

Neither of those suggestions communicates clearly what they do. I would have no idea what | ... meant, and |||| is even worse. Thank you for making the suggestion, though. Although I disagree with this one, I'm glad that you're interested in helping to improve F# and make it easier to learn. Please continue to make suggestions — your idea in #604 was quite interesting.

rmunn commented Sep 11, 2017

Neither of those suggestions communicates clearly what they do. I would have no idea what | ... meant, and |||| is even worse. Thank you for making the suggestion, though. Although I disagree with this one, I'm glad that you're interested in helping to improve F# and make it easier to learn. Please continue to make suggestions — your idea in #604 was quite interesting.

@Rickasaurus

This comment has been minimized.

Show comment
Hide comment
@Rickasaurus

Rickasaurus Sep 13, 2017

You can already do something like this pattern matching in a normal lambda:

let a = Some 10
a |> fun (Some x) -> x  

Rickasaurus commented Sep 13, 2017

You can already do something like this pattern matching in a normal lambda:

let a = Some 10
a |> fun (Some x) -> x  
@abelbraaksma

This comment has been minimized.

Show comment
Hide comment
@abelbraaksma

abelbraaksma Sep 19, 2017

While I find the idea intriguing, I don't think the benefits outweigh the costs. I have often struggled with how to clearly finish unfinished cases. I usually end up doing something like the following:

let (|MySome|) x = match x with Some x -> x | _ -> failwith "never"

let testMeAlt1 =
    let a = Some 10
    a |> function MySome x -> x 

But if quirky syntax is your thing, you could perhaps make it abundantly clear writing something like the following instead.

let inline (!!!!) _ = failwith "never"

let testMeAlt2 =
    let a = Some 10
    a |> function Some x -> x | _ -> !!!!()

If you want to make it (much) clearer what is going on, this approach might be better:

let (|Never|) x = Unchecked.defaultof<_>        // or raise exception

let testMeAlt3 =
    let a = Some 10
    a |> function Some x -> x  | Never x -> x

All these approaches can be made to work with any DU. And while I have never used the last approach above, I do like to occasionally use the first approach. I typically name such methods OnlyXXX where XXX is the DU type it should match.

Still, apart from rare cases, needing this is often a sign of code smell, and for code smell you shouldn't have specific syntax as people might actually use it and ruin the safety of their code (warning 25 is there for a reason!).

abelbraaksma commented Sep 19, 2017

While I find the idea intriguing, I don't think the benefits outweigh the costs. I have often struggled with how to clearly finish unfinished cases. I usually end up doing something like the following:

let (|MySome|) x = match x with Some x -> x | _ -> failwith "never"

let testMeAlt1 =
    let a = Some 10
    a |> function MySome x -> x 

But if quirky syntax is your thing, you could perhaps make it abundantly clear writing something like the following instead.

let inline (!!!!) _ = failwith "never"

let testMeAlt2 =
    let a = Some 10
    a |> function Some x -> x | _ -> !!!!()

If you want to make it (much) clearer what is going on, this approach might be better:

let (|Never|) x = Unchecked.defaultof<_>        // or raise exception

let testMeAlt3 =
    let a = Some 10
    a |> function Some x -> x  | Never x -> x

All these approaches can be made to work with any DU. And while I have never used the last approach above, I do like to occasionally use the first approach. I typically name such methods OnlyXXX where XXX is the DU type it should match.

Still, apart from rare cases, needing this is often a sign of code smell, and for code smell you shouldn't have specific syntax as people might actually use it and ruin the safety of their code (warning 25 is there for a reason!).

@xp44mm

This comment has been minimized.

Show comment
Hide comment
@xp44mm

xp44mm Sep 21, 2017

@abelbraaksma thank you for your knowledge. i like the first method. i never think to it. both 2nd and 3rd are longer than | _ -> failwith "", maybe i can define a module named Only, and summary every cases.

xp44mm commented Sep 21, 2017

@abelbraaksma thank you for your knowledge. i like the first method. i never think to it. both 2nd and 3rd are longer than | _ -> failwith "", maybe i can define a module named Only, and summary every cases.

@abelbraaksma

This comment has been minimized.

Show comment
Hide comment
@abelbraaksma

abelbraaksma Sep 21, 2017

Yes, that sounds like a viable approach for your use case (btw, if you count the characters, the other approaches are not smaller, unless you mean the added extension functions, but these can be used for any situation, so you only have to define them once).

Either way, don't forget that if others need to understand your code, good naming of active patterns is important. Also, having a clear failwith "unexpected program point" (or putting this in a specific exception) is a good way of telling anyone in your team what is supposed to happen.

If you agree that adding extra syntax sugar here has no significant benefits, feel free to close the thread (click [Close And Comment] button).

abelbraaksma commented Sep 21, 2017

Yes, that sounds like a viable approach for your use case (btw, if you count the characters, the other approaches are not smaller, unless you mean the added extension functions, but these can be used for any situation, so you only have to define them once).

Either way, don't forget that if others need to understand your code, good naming of active patterns is important. Also, having a clear failwith "unexpected program point" (or putting this in a specific exception) is a good way of telling anyone in your team what is supposed to happen.

If you agree that adding extra syntax sugar here has no significant benefits, feel free to close the thread (click [Close And Comment] button).

@xp44mm

This comment has been minimized.

Show comment
Hide comment
@xp44mm

xp44mm Sep 21, 2017

@abelbraaksma maybe just code following

let never<'a> = failwith<'a> "never"

let a = Some 10
let b =
    match a with
    | Some x -> x
    | _ -> never

i used to read the fyacc generated code, the code have a lot of not complete pattern match. and i also have many match is not complete. may i get the first line from fsharp.core in the future?

xp44mm commented Sep 21, 2017

@abelbraaksma maybe just code following

let never<'a> = failwith<'a> "never"

let a = Some 10
let b =
    match a with
    | Some x -> x
    | _ -> never

i used to read the fyacc generated code, the code have a lot of not complete pattern match. and i also have many match is not complete. may i get the first line from fsharp.core in the future?

@xp44mm xp44mm closed this Sep 21, 2017

@abelbraaksma

This comment has been minimized.

Show comment
Hide comment
@abelbraaksma

abelbraaksma Sep 22, 2017

may i get the first line from fsharp.core in the future?

Not sure what you mean, but if you mean to make let never<'a> = failwith<'a> "never" a function in the core, that's most likely not going to happen (though it is not me who has any say in that). It's a simple one liner that anyone can make, and it contains user-specific strings that would end up being part of the exception. If we were to make it useful, we would have to parameterize it, i.e. let never<'a> s = failwith<'a> s, which makes it synonymous to failwith, which is already there. So there's no gain by adding to the language what's already in the language.

As an example, many of my projects contain something like the following, where I raise a NotSupportedException when a program condition occurs that was unexpected (i.e., like your example with never). But since each situation is different, even in my own code, I still like to add a message and not force the message to be the same all the time.

/// Raises a not-supported exception for operations that are not supported or unexpected
let inline raiseNotSupported message =
    sprintf "Unexpected operation or program point: %s" message
    |> NotSupportedException
    |> raise

And then, if for one module I need the same messages anyway, I can always simplify it:

let inline raiseNotSupported() = raiseNotSupported "this was really unexpected!"

abelbraaksma commented Sep 22, 2017

may i get the first line from fsharp.core in the future?

Not sure what you mean, but if you mean to make let never<'a> = failwith<'a> "never" a function in the core, that's most likely not going to happen (though it is not me who has any say in that). It's a simple one liner that anyone can make, and it contains user-specific strings that would end up being part of the exception. If we were to make it useful, we would have to parameterize it, i.e. let never<'a> s = failwith<'a> s, which makes it synonymous to failwith, which is already there. So there's no gain by adding to the language what's already in the language.

As an example, many of my projects contain something like the following, where I raise a NotSupportedException when a program condition occurs that was unexpected (i.e., like your example with never). But since each situation is different, even in my own code, I still like to add a message and not force the message to be the same all the time.

/// Raises a not-supported exception for operations that are not supported or unexpected
let inline raiseNotSupported message =
    sprintf "Unexpected operation or program point: %s" message
    |> NotSupportedException
    |> raise

And then, if for one module I need the same messages anyway, I can always simplify it:

let inline raiseNotSupported() = raiseNotSupported "this was really unexpected!"
@xp44mm

This comment has been minimized.

Show comment
Hide comment
@xp44mm

xp44mm Sep 22, 2017

i like your passion and your knowledge. i hope to standard never, because i don't want to break my thought about bussiness logic to be more fluent. after module separate and unit test, the exception prompt has been not very important as past. especially in a coder's internal code. too many exceptions all over code is less than just several exceptions indeed important.
so, i have given up this propose. i explain my opinion.

xp44mm commented Sep 22, 2017

i like your passion and your knowledge. i hope to standard never, because i don't want to break my thought about bussiness logic to be more fluent. after module separate and unit test, the exception prompt has been not very important as past. especially in a coder's internal code. too many exceptions all over code is less than just several exceptions indeed important.
so, i have given up this propose. i explain my opinion.

@abelbraaksma

This comment has been minimized.

Show comment
Hide comment
@abelbraaksma

abelbraaksma Sep 22, 2017

too many exceptions all over code is less than just several exceptions indeed important.

You will find that coding with F# requires hardly any exceptions. And a good coding practice is to define them centrally, but give specific messages for each situation (which you can define in a resource file).

F# 4.1 added a few new easy-access exceptions for the common case, but specific cases like these naturally aren't in there, as they only apply to one or a few situations. Since you can override everything in F# and even redefine failwith to become a function that doesn't take a string, I think you'll have plenty of tools at hand to write your own libraries. That's what programming is about. And if you think your library makes sense to be used by others, you can share it here on github, as you do now, or elsewhere. A good starting point for sharing your snippets is: http://www.fssnip.net/.

abelbraaksma commented Sep 22, 2017

too many exceptions all over code is less than just several exceptions indeed important.

You will find that coding with F# requires hardly any exceptions. And a good coding practice is to define them centrally, but give specific messages for each situation (which you can define in a resource file).

F# 4.1 added a few new easy-access exceptions for the common case, but specific cases like these naturally aren't in there, as they only apply to one or a few situations. Since you can override everything in F# and even redefine failwith to become a function that doesn't take a string, I think you'll have plenty of tools at hand to write your own libraries. That's what programming is about. And if you think your library makes sense to be used by others, you can share it here on github, as you do now, or elsewhere. A good starting point for sharing your snippets is: http://www.fssnip.net/.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment