Are Arrays in Elm fragile?

Has anyone else found Arrays in Elm to be ‘fragile’?

The other day I managed to ‘crash’ the compiler with a thread blocked indefinitely in an MVar operation error, by forgetting the element in Array.set
Viz, Array.set index array instead of Array.set index element array.

And now I have one of those hard to track down The 1st argument to 'element' is not what I expect errors — caused (?) by an Array.get — which doesn’t make sense.

(I’ve replaced some of the usual/obvious with ‘…’ for brevity)

With the following in my code…

type alias Categories =
   Array (Maybe String)

type alias Model =
   { categories : Categories
   , ...
   }

update msg model =
   case msg of
      ...
      EditThisCategory catID ->
         let
            catText =
               case Array.get catID model.categories of
                  Nothing -> ""
                  Just txt -> txt
         in
--         (  {  model |  categoryBeingEdited = Just catID
--            ,           categoryTextBeingEdited = catText
--            }
--         ,  Cmd.none 
--         )
         ( model, Cmd.none ) 
      ...

…I get the following when I compile

 $ elm make ./src/Main.elm --output ./main.js --debug
Detected problems in 1 module.
-- TYPE MISMATCH -------------------------------------------------- src/Main.elm

The 1st argument to `element` is not what I expect:

34|   Browser.element
35|>      {  ... }

This argument is a record of type:

    { init : Flags -> ( Model, Cmd Msg )
    , subscriptions : Model -> Sub Msg
    , update :
          Msg
          -> { categories : Array String
...          }
          -> ( { categories : Array String
...            }
             , Cmd Msg
             )
    , view : Model -> Html Msg
    }

But `element` needs the 1st argument to be:

    { init : Flags -> ( Model, Cmd Msg )
    , subscriptions : Model -> Sub Msg
    , update : Msg -> **Model** -> ( **Model**, Cmd Msg )
    , view : Model -> Html Msg
    }

The code I’ve presented in update appears to be where the bug is because, if I comment out everything from let to in — leaving only ( model, Cmd.none ) — it all compiles.

Have I made a silly error, or is something strange going on?

I do have two paths in update that end in a direct call to update
that is, update msgX modelY instead of ( model, cmd ),
if this is relevant. But they were there, and compiling fine, before this error occurred.

1 Like

For the last error, compare your model

type alias Model =
   { categories : Array (Maybe String)
   , ...
   }

with what elm thinks it is in the error message:

{ categories : Array String
, ...
}

Elm thinks the array contains String, not Maybe String because Array.get returns Maybe element where element in your case should be itself Maybe String.
So a correct case would be

let
    catText =
        case Array.get catID model.categories of
            Nothing -> ""
            Just Nothing -> ""
            Just (Just txt) -> txt
in

Also in general, the MVar errors are usually related to missing or incorrect let type annotations and not Array-specific. Feel free to show the example if you can’t get the MVar error to disappear

5 Likes

Thank you, @lue-bird

So it’s option a) silly error — tripped up on nested Maybes.

I managed to get the same error by converting the Array to a Dictionary and getting the same error, so I was slowly inching towards the answer :laughing:

Interesting to know what (typically) causes MVar errors — I’ll check the type annotations in future :slightly_smiling_face:

1 Like

One thing that helped me recently with wrapping my head around typing errors was to go and extract parts of my logic into let-assignments (which you already did) but then also explicitly type those parts to guide me and the compiler on what i think i want here vs what the compiler knows i actually have.

So in your situation i think if your code had been

catText : String
catText =
  -- ... and so on

you would have gotten a very specific error message instead of the “big one” you got and struggled with - and hopefully you could’ve worked from there.

Something i had to remind myself about lately, so hopefully not “too basic” for me to point out :sweat_smile:

6 Likes

Thank you @IloSophiep. Not too basic at all — a great idea.

I typically add type detail to functions that have at least one parameter, but usually leave them off for those that don’t. In future I shall add them everywhere :slight_smile:

I agree this (adding to type signatures to pretty much all definitions, including those within a let-in) is good practice - but it’s not universally agreed.

Here is my logic in the form of a blog post: Let signatures | Allanderek's blog

Someone with the opposite view point can defend it better than I can, but I think the rough idea is that when definitions become large/complicated enough to demand a type signature they should be extracted out to top-level definitions. I think that moves the discussion to why a top-level definition would be better than a local one (with good arguments on both sides). So I still end up with a position along the lines of; okay some local definitions perhaps should be extracted out to the top-level, but not all of them, and those that remain should have a type signature.

But note, I can see some value in having a heuristic that makes you at least consider moving a definition out to the top-level as soon as you think you need a type-signature. However, I basically put type-signatures on all definitions, even pretty trivial ones.

1 Like

Also worth mentioning that this is a compiler bug. If the bug was fixed then there should be no need to put a type signature - not just in let…in but anywhere. In theory at least, Elms type system is fully decidable both ways, inference and checking.

2 Likes

The MVar thing is a compiler bug, but are you saying that it’s a compiler bug that @AlanQ didn’t get a better error message? I don’t think that’s what you meant, but it’s sort of how your comment reads (at least to me).

I meant that its a compiler bug when you get the MVar runtime exception during compilation.

Okay cool, but then we don’t know that that had anything to do with type signatures, as in we do not know that adding a type signature would have solved that.

Interesting to know what (typically) causes MVar errors — I’ll check the type annotations in future :slightly_smiling_face:

MVar error is a dead lock internal in compiler. It’s a bug in compiler which should not happen but no software is perfect.

In elm these are usually part of the most complicated part of compiler which is type cheking and inference.

Are arrays in fragile?

No and definitely not in a way you indicate. This error is not at all related to Array. This part of compiler is not concerned with existence of type like array. It’s just coincidence that the type in question was an Array/Dict in your case. You can define your own type, misuse it in same way to trigger the same error.

1 Like