compiler icon indicating copy to clipboard operation
compiler copied to clipboard

Elm Reactor should pass `flags: null`

Open juliusl opened this issue 2 years ago • 3 comments

Quick Summary: To develop apps with JS-Interop it would be great if reactor would set init to null, so that on the Elm side you can declare main like main: Program (Maybe <some-type>) Model Msg.

Currently it doesn't set any value which means that undefined is returned, and this error will be shown

(1) Problem with the given value:
    
    undefined
    
    Expecting null

SSCCE

Try this in playground --

import Html exposing (text)
import Browser

main: Program (Maybe Int) Model Msg
main =
    Browser.document 
      { init = \a -> ({}, Cmd.none)
      , view = \m -> { title = "t", body = [text "hi"]}
      , update = \_ _ -> ({}, Cmd.none)
      , subscriptions = \_ -> Sub.none
      }

type alias Model = {}
type Msg = None

On a side note, I sort of expected to be able to do this:

type alias Undefined = () 

type Startup = Undefined | Maybe Int

main : Program Startup Model Msg 

But that results in:

BAD FLAGS - Your `main` program wants a `Startup` value from JavaScript.

21| main = 
    #^^^^#
I cannot handle that. The types that CAN be in flags include:

    Ints, Floats, Bools, Strings, Maybes, Lists, Arrays, tuples, records, and
    JSON values.

Since JSON values can flow through, you can use JSON encoders and decoders to
allow other types through as well. More advanced users often just do everything
with encoders and decoders for more control and better errors.

Which is reasonable, but seems a bit inconsistent when () compiles with Program () Model Msg.

juliusl avatar Apr 06 '22 21:04 juliusl

Thanks for reporting this! To set expectations:

  • Issues are reviewed in batches, so it can take some time to get a response.
  • Ask questions in a community forum. You will get an answer quicker that way!
  • If you experience something similar, open a new issue. We like duplicates.

Finally, please be patient with the core team. They are trying their best with limited resources.

github-actions[bot] avatar Apr 06 '22 21:04 github-actions[bot]

type alias Undefined = ()

type Startup = Undefined | Maybe Int

type alias Undefined = () has no effect on the Startup declaration. Startup happens to have a type constructor called Undefined (which takes no parameters). The type alias Undefined is not referenced anywhere and can be removed without changing anything.

Similarly, Maybe is declared as a type constructor for Startup in this example, and it takes an Int parameter - it happens to be called Maybe but it does not use Elm's built in Maybe type.

type in Elm works a lot differently than in TypeScript - basically it's for creating "tagged union" types (unlike with TS, you don't need to have an object with a special "determinant" field). Each option for the type (the first word) is your own custom type constructor function, while following words are types. For example:

type = Fruit String | Vegetable String | Other

means you can declare variables like this

apple = Fruit "Apple"
broccoli = Vegetable "Broccoli"
wheat = Other

If it was possible to have a fruit with no name, you could do:

type = Fruit (Maybe String) | Vegetable String | Other

apple = Fruit (Just "Apple")
secretFruit = Fruit Nothing
broccoli = Vegetable "Broccoli"
wheat = Other

This is how you'd use an Elm Maybe in a custom type.

For flags, you need to do something a bit more involved in order to accept data with different shapes... I'm not super knowledgeable with the different techniques for decoding flags so I'll stop here and just drop the Elm guide's link on them for convenience: https://guide.elm-lang.org/interop/flags.html

Hope this helps!

adamdicarlo avatar Sep 01 '22 22:09 adamdicarlo

@adamdicarlo Thanks for replying. That makes sense. Let me rephrase my example so I can get some clarity.

What is this type known as, () ?

And if I rewrite my example, then would this expression make sense,

type Startup = NoFlags () | Edit (Maybe Int) 

So what I'm looking for is the ability to do this,

type Startup = NoFlags () | IntFlag (Maybe Int) 

main: Program Startup Model Msg
...

The reason is because this is valid,

main: Program () Model Msg

and this is valid,

main: Program (Maybe Int) Model Msg

But the reason the second example wouldn't work in the playground is because the playground doesn't pass in null as a flag parameter, and by extension the reactor does not pass null as well. But if you were to write the html on your own and pass null it would work. But if you don't pass anything (undefined), then () is expected.

So I guess the limitation here is that main (elm compiler) doesn't accept union-types as a flag parameter?

juliusl avatar Nov 11 '22 23:11 juliusl