-
Notifications
You must be signed in to change notification settings - Fork 359
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 the empty function never : Never -> a #593
Conversation
Another possible use case occurred in this thread. |
Elaborating on the use case from the mentioned thread: One may model the case that one has some html that is static (in the sense it never gives rise to an event) by using type |
|
You would also have to add the function name to a |
Signed-off-by: Anders Kaseorg <[email protected]>
Added to |
Historically when Evan has asked for a motivating example, he's meant something of the form "I was building something useful, and hit this roadblock." He's also generally asked questions about existing solutions and attempted workarounds; adding a new capability has been largely treated as a last resort, only after all other options have been exhausted. Does that clarify what he's looking for? |
@andersk, in line with the directions under https://github.com/elm-lang/core/blob/master/CONTRIBUTING.md#adding-new-functions, I propose you move this pull request to https://github.com/elm-community/basics-extra (which I just created, to welcome this contribution as the first one in the package). |
@rtfeldman I’m happy to expand on my particular use case, but I don’t think there are additional interesting details here; this is a generic problem with I am writing a full screen application that needs to know the window size. So I add import Html
import Html.App as Html
import Task
import Window
type Msg = Resize Window.Size
type alias Model = { size : Window.Size }
main : Program Never
main =
Html.program
{ init =
( { size = Window.Size 0 0 }, Task.perform ??? Resize Window.size )
, update =
\msg model ->
case msg of
Resize size -> ( { size = size }, Cmd.none )
, view =
\model -> Html.text (toString model.size)
, subscriptions =
\model -> Window.resizes Resize
}
If you strip away all the details, this problem is equivalent to that of writing a function The fact that a destructor is not provided for the standard empty type |
@jvoigtlaender Thanks, but this pull request cannot be moved as written, because only the I do not propose exporting the type Never -- no constructors
never : Never -> a
never n = case n of
-- no cases Among the available options:
I still think the first is cleanest. What do you think? |
My proposal (see also a recent exchange on the mailing list) is to add the function to basics-extra with this definition: never n = never n |
Submitted as elm-community/basics-extra#1. Keeping this issue open to track the request to move |
We'd discussed having a That said, without I agree that people would not intuitively realize |
@rtfeldman Besides, why should a hypothetical solution that only works with |
Ah, good point! Let me try to summarize the discussion. Here are three options, and their drawbacks: Option 1:
|
@rtfeldman Sure. I would add that if |
Hopefully not distracting from the discussion here, but Richard, when you say
you mean the elm/compiler#873 discussed over there, or something else? The elm/compiler#873 doesn't apply, I think. |
@jvoigtlaender That was what I meant, but 👍 if you think it doesn't apply. 😄 @andersk Fair point about the potential for misusing It occurs to me that there's a fourth option: Option 4:
|
@rtfeldman, that's exactly the thing from this proposal on the mailing list, right? Yes, this could be put into Of course, one can make special versions of |
Even if for some reason you think we should duplicate all functions like |
Why is this better than
The first two points on Elm's official API design guidelines are: If it seems like I'm pushing back so hard on abstracting this without a specific use case, it's because that is explicitly and unequivocally the Elm way to approach API design. :) |
In my very first comment above, I linked to a thread on the mailing list. There, someone wanted to express that their model type carries static-only html. How to do that? With type alias Model =
{ staticHtml : Html Never
, someOtherFields : ... } With your suggestion: type alias Model a =
{ staticHtml : Html a
, someOtherFields : ... } Spot the difference? It's suddenly necessary to parametrize the Moreover, your solution is no solution at all if I want to express that some function in my component accepts only effect-free Hint: The following is not the solution. function : Html a -> Whatever To that function, the caller can pass arbitrarily effectful But the following is a solution. function : Html Never -> Whatever Nobody can pass an |
@rtfeldman I am not asking for a new abstraction. I’m simply asking that the existing If Elm had rank n types (elm/compiler#1039), one could replace (Additionally, my previous point about using |
Great points all around! I'm sold on a One final note:
Another recurring theme of Evan's is "don't block on me." Since the Personally I would recommend using Here's a revised summary: MotivationThere are two problematic use cases that currently have no good solution: Html.program
{ init =
( { size = Window.Size 0 0 }, Task.perform _____ Resize Window.size ) What goes in the blank? The other problematic use case is this: type alias Model =
{ staticHtml : Html Never
, someOtherFields : ... } You can write this, but you can never use This comes up in reusable components. For a concrete example of this, see SolutionIntroduce a function Html.program
{ init =
( { size = Window.Size 0 0 }, Task.perform _____ Resize Window.size ) Complete working example: import Html exposing (..)
import Html.App
import Html.Events exposing (..)
main =
view model
type alias Model =
{ staticHtml : Html Never }
type Msg = NoOp
model : Model
model =
{ staticHtml = i [] [ text "Hello, World!" ] }
view : Model -> Html Msg
view model =
button [ onClick NoOp ]
[ Html.App.map never model.staticHtml ]
never : Never -> a
never a = never a Unsatisfactory AlternativesYou can use type alias Model a =
{ staticHtml : Html a
, someOtherFields : ... } This in turn means that every alias using this one will also need a type variable, which is annoying and can potentially impact a ton of code. There are several options that would address the "
|
I’m not sure what you mean. This pull request is a |
Gah, wrong link! I meant https://github.com/elm-community/basics-extra/pull/1/files ...which I now see uses it, and has the other one as a comment. Never mind. 😄 |
That one already uses |
This exists now: http://package.elm-lang.org/packages/elm-community/basics-extra/latest/Basics-Extra#never. Next, should |
For the |
@rtfeldman, is the (That repository doesn't have GitHub issues enabled, hence I'm asking here. I could of course simply make a PR against that repository, but don't know whether it's open for additions. Also, it's not yet updated to 0.16, so maybe it's actually on road to deprecation. Should a new |
@jvoigtlaender happy to accept a PR for that! Please @ me so I see it. 😄 |
So there are now three "hidden" uses of
each with an example application in their documentation. @andersk, you originally mentioned that one might want to use the function in |
I imagine its use case would be similar to init : Flags -> (Model, Cmd Never)
update : Never -> Model -> (Model, Cmd Never)
subscriptions : Model -> Sub Never
view : Model -> Html Never and you want to compose it into a larger component with a nonempty message type. Perhaps you could just throw away its There may also be legitimate However, I remain skeptical of the premise that any uses of |
Hmm. I just noticed that the |
I don't think that makes sense. It would be impossible to ever call the function
That reminds me of https://github.com/evancz/elm-effects/issues/28. In the 0.17 world, that would be a function So I am now actually skeptical one would ever have a use for the None of that means I would not also be happy to have About your last comment, regarding |
Sure, such a static subcomponent scenario seems somewhat strained in solitude. (One could imagine writing a combinator that takes a component as an argument and is polymorphic in the message type, and then wanting to instantiate it with a static subcomponent. But I agree that this particular train of thought involves a lot of speculation and isn’t likely to justify a specialized API for |
Tracking in #322. Very easy to add, so makes sense to make the decision before writing the code. |
There seems to be no way to pattern-match on the empty type
Never
; this adds a canonical way to do so. An example where this is needed isTask.perform never : (a -> msg) -> Task Never a -> Cmd msg
.(I suppose that given a polymorphic
Task never a
, one could use something likeTask.perform identity : (a -> msg) -> Task a a -> Cmd msg
instead. But that’s confusing.)This is a more general solution to #578, which would now be solved by
Cmd.map never : Cmd Never -> Cmd msg
. It’s safer than making the user write aDebug.crash
call because the type system prevents it from being accidentally put in a place where it might ever receive a value.(I previously called this function
absurd
, after Haskell’sData.Void.absurd
.)