Skip to content
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

Wrong "expected" type from the annotation #1192

Closed
Janiczek opened this issue Nov 20, 2015 · 26 comments
Closed

Wrong "expected" type from the annotation #1192

Janiczek opened this issue Nov 20, 2015 · 26 comments
Labels
Milestone

Comments

@Janiczek
Copy link

The "expected" and "actual" rows are the same when they probably shouldn't be.

The type annotation for `shrinker` does not match its definition.

17│ shrinker : Shrinker (List (Maybe MyType))
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The type annotation is saying:

    Shrinker (Maybe MyType)

But I am inferring that the definition has this type:

    Shrinker (Maybe MyType)

I would have expected it to say:

-The type annotation is saying:
-
-    Shrinker (Maybe MyType)
+The type annotation is saying:
+
+    Shrinker (List (Maybe MyType))

The minimal code:

module Main (..) where

import Lazy.List exposing (empty)
import Shrink exposing (Shrinker, maybe)


type MyType
    = A
    | B


shrinkerMyType : Shrinker MyType
shrinkerMyType action =
    empty


shrinker : Shrinker (List (Maybe MyType))
shrinker =
    maybe shrinkerMyType

The relevant dependencies:

    "dependencies": {
        "NoRedInk/elm-lazy-list": "2.0.0 <= v < 3.0.0",
        "NoRedInk/elm-shrink": "1.0.3 <= v < 2.0.0",
        "elm-lang/core": "3.0.0 <= v < 4.0.0",
    },
    "elm-version": "0.16.0 <= v < 0.17.0"
@jacob-tock
Copy link

I ran into another instance of what is probably the same bug, plus maybe another related bug. In Elm 0.16, the following code:

module Buggy where

import Debug

type alias Alias a = a -> a

convert : (a -> b) -> Alias b -> Alias a
convert _ = Debug.crash "unimplemented"

applyToSecond : Alias b -> Alias (a, b)
applyToSecond _ = Debug.crash "unimplemented"

wrongTypeFunction : Int -> String
wrongTypeFunction _ = Debug.crash "unimplemented"

badError : Alias value -> Alias Bool
badError alias =
  convert wrongTypeFunction (applyToSecond alias)

produces two error messages, both displaying the same type as both expected and actual:

-- TYPE MISMATCH ----------------------------------------------------- Buggy.elm

The type annotation for `badError` does not match its definition.

16│ badError : Alias value -> Alias Bool
               ^^^^^^^^^^^^^^^^^^^^^^^^^
The type annotation is saying:

    Alias b -> Alias Int

But I am inferring that the definition has this type:

    Alias b -> Alias Int

-- TYPE MISMATCH ----------------------------------------------------- Buggy.elm

The 2nd argument to function `convert` is causing a mismatch.

18│   convert wrongTypeFunction (applyToSecond alias)
                                 ^^^^^^^^^^^^^^^^^^^
Function `convert` is expecting the 2nd argument to be:

    Alias ( b, c )

But it is:

    Alias ( b, c )

Hint: I always figure out the type of arguments from left to right. If an
argument is acceptable when I check it, I assume it is "correct" in subsequent
checks. So the problem may actually be in how previous arguments interact with
the 2nd.

@jvoigtlaender
Copy link
Contributor

@evancz, this looks like a rather serious regression in 0.16. Have you seen it? (I know you have notifications turned off for some repositories.)

@jvoigtlaender
Copy link
Contributor

I said "regression" because in 0.15 the reporting here, while using different words, gives correct information where 0.16 doesn't.

@jefelino
Copy link

jefelino commented Dec 8, 2015

Here's another simpler example of what is probably the same bug. (I originally reported this at elm/error-message-catalog/issues/69, but jvoigtlaender pointed out that wasn't the right place for it.)

type alias F a = a

wrong : F a -> F b
wrong = identity

This produces the following error message:

The type annotation for `wrong` does not match its definition.
The type annotation is saying:
    F b -> F b
But I am inferring that the definition has this type:
    F b -> F b
Hint: A type annotation is clashing with itself or with a sub-annotation. This
can be particularly tricky, so read more about it.
<https://github.com/elm-lang/elm-compiler/blob/0.16.0/hints/type-annotations.md>

@mgold
Copy link
Contributor

mgold commented Dec 8, 2015

A lot of people have run across this. Would be great to get a fix out.

@evancz
Copy link
Member

evancz commented Dec 13, 2015

Is the shared characteristic between all these examples that they use a type alias?

Can someone link to the definition of Shrinker so we can see all these side-by-side?

@jvoigtlaender
Copy link
Contributor

I guess it's this one: http://package.elm-lang.org/packages/NoRedInk/elm-shrink/1.0.3/Shrink#Shrinker

So yes, also a type alias.

@evancz
Copy link
Member

evancz commented Dec 13, 2015

Can we try to figure out cases where this does and does not happen? Like,
an example where "changing this one thing" flips it to breaking.

On Sunday, December 13, 2015, Janis Voigtländer [email protected]
wrote:

I guess it's this one:
http://package.elm-lang.org/packages/NoRedInk/elm-shrink/1.0.3/Shrink#Shrinker

So yes, also a type alias.


Reply to this email directly or view it on GitHub
#1192 (comment)
.

Sent from Gmail Mobile

@jvoigtlaender
Copy link
Contributor

Try this, which has wrong behavior:

type alias F a = a

f : F a -> a
f = identity

vs. the following ones, both of which have the correct behavior:

type alias F a = a

f : F Int -> Int
f = identity

and

type alias F = Int

f : F -> Int
f = identity

So the error conditions seem to be the following two coming together:

  • a type alias definition with a type variable argument
  • a polymorphic use of that type alias

@rhofour
Copy link

rhofour commented Dec 28, 2015

I have an example that might be related which doesn't seem to use a type alias.

genGrid : List (number, number) -> Dict.Dict (number, number) ()
genGrid coords = 
    Dict.fromList (List.map (\x -> (x, ())) coords)

This gives an error as-is, but is fixed if you replace (number, number) with comparable.

@jvoigtlaender
Copy link
Contributor

@rhofour, this might be a separate error that has to do with comparable not being treated/propagated properly by the type checker.

Specifically, these two examples might be better suited to get a hang on what goes wrong with your example as well:

import Set

f : List number -> Set.Set number
f = Set.fromList

vs.

import Set

f : List (Int,number) -> Set.Set (Int,number)
f = Set.fromList

@jvoigtlaender
Copy link
Contributor

I have opened a separate issue (#1281) related to @rhofour's case. I think that other error might influence what is shown in his case. So for the issue at hand here, I still conjecture that, in response to @evancz's question "when exactly does this bad thing happen", the answer is: "when a type alias definition with a type variable argument is used somewhere in a still polymorphic fashion".

@ccapndave
Copy link

Has this somehow got worse in 0.17 (or do the new features make it more likely to occur)? I ask because I have never noticed this bug before, but since I started playing with 0.17 I have noticed it multiple times in multiple situations.

@jvoigtlaender
Copy link
Contributor

It has come up pretty often before 0.17 already, as you can see above...

But, one reason you may see it come up particularly often with 0.17 is that more stuff is type aliased than was before. Since the bug is tied to type alias definitions, the more type aliases occur in core (like newly Task.Task), the more often this bug will surface.

@evancz
Copy link
Member

evancz commented Apr 27, 2016

Cool, I have seen this a couple times with 0.17 during development and wasn't sure what was up. Thank you for the http://sscce.org/

I'll see if I can figure it out.

@evancz
Copy link
Member

evancz commented Apr 27, 2016

Alright, it was a dumb mistake in which I unified variables before I checked all the subparts. This meant that the subparts would still fail, but only after the types were set to be the same.

Anyway, here's the new result from the example that didn't need any imports:

-- TYPE MISMATCH ------------------------------------------------------- bug.elm

The type annotation for `badError` does not match its definition.

23│ badError : Alias value -> Alias Bool
               ^^^^^^^^^^^^^^^^^^^^^^^^^
The type annotation is saying:

    Alias b -> Alias Bool

But I am inferring that the definition has this type:

    Alias b -> Alias Int

-- TYPE MISMATCH ------------------------------------------------------- bug.elm

The 2nd argument to function `convert` is causing a mismatch.

25│   convert wrongTypeFunction (applyToSecond alias)
                                 ^^^^^^^^^^^^^^^^^^^
Function `convert` is expecting the 2nd argument to be:

    Alias String

But it is:

    Alias ( b, c )

Hint: I always figure out the type of arguments from left to right. If an
argument is acceptable when I check it, I assume it is "correct" in subsequent
checks. So the problem may actually be in how previous arguments interact with
the 2nd.

@evancz evancz closed this as completed Apr 27, 2016
@Janiczek
Copy link
Author

That's great news, thanks Evan!
On Apr 28, 2016 12:47 AM, "Evan Czaplicki" [email protected] wrote:

Alright, it was a dumb mistake in which I unified variables before I
checked all the subparts. This meant that the subparts would still fail,
but only after the types were set to be the same.

Anyway, here's the new result from the example that didn't need any
imports:

-- TYPE MISMATCH ------------------------------------------------------- bug.elm

The type annotation for badError does not match its definition.

23│ badError : Alias value -> Alias Bool
^^^^^^^^^^^^^^^^^^^^^^^^^
The type annotation is saying:

Alias b -> Alias Bool

But I am inferring that the definition has this type:

Alias b -> Alias Int

-- TYPE MISMATCH ------------------------------------------------------- bug.elm

The 2nd argument to function convert is causing a mismatch.

25│ convert wrongTypeFunction (applyToSecond alias)
^^^^^^^^^^^^^^^^^^^
Function convert is expecting the 2nd argument to be:

Alias String

But it is:

Alias ( b, c )

Hint: I always figure out the type of arguments from left to right. If an
argument is acceptable when I check it, I assume it is "correct" in subsequent
checks. So the problem may actually be in how previous arguments interact with
the 2nd.


You are receiving this because you authored the thread.
Reply to this email directly or view it on GitHub
#1192 (comment)

@jvoigtlaender
Copy link
Contributor

The issue isn't fixed. The one example above is handled correctly now, but others that occur in comments above are not. Specifically, the SSCCE I gave:

type alias F a = a

f : F a -> a
f = identity

Should be accepted, but isn't on 0.17 http://elm-lang.org/try.

@evancz
Copy link
Member

evancz commented May 12, 2016

New issue with SSCCE.

@vrescobar
Copy link

vrescobar commented Sep 19, 2016

EDIT: as jvoigtlaender pointed, the error I post was actually in my own code, sorry for posting that!

I think I also hit this bug. I do not truly yet understand the whole type system, but adding a "case" statement for a let definition makes the compiler infer a different type and fail.

I try to bring an example of code that may fail or work depending on a let statement.

Compiler error:

$ elm-make Inference.elm 
-- TYPE MISMATCH ------------------------------------------------- Inference.elm

The type annotation for `expand_item` does not match its definition.

12| expand_item: TodoItem -> Html.Html msg
                 ^^^^^^^^^^^^^^^^^^^^^^^^^
The type annotation is saying:

    { ..., state : ... } -> Html a

But I am inferring that the definition has this type:

    { a | ..., itemState : ... } -> Html b

Detected errors in 1 module.                                        

Code (depending how you define the "is_checked" at lines 16/17 the type inferred is different and the compilation fails or succeeds:

module Inference exposing (..)

import Html exposing (..)
import Html.Attributes exposing (..)

type TodoState = ToDo | Done
type alias TodoItem =
  { message: String
  , state: TodoState
  }

expand_item: TodoItem -> Html.Html msg
expand_item item =
  let
    todo_message = item.message
    --is_checked = True -- comment this definition to make it work
    --is_checked = (case item.itemState of
    --                ToDo -> False
    --                Done -> True)
  in
    li
    [ class "completed" ]
    [ div
      [ class "view" ]
      [ input
        [ class "toggle", type' "checkbox", checked is_checked ]
        []
      , label
        []
        [ text todo_message ]
      ]
    ]

view: List TodoItem -> Html.Html msg
view initial_model =
  div [] (List.map expand_item initial_model)

main =
  view [{message="hello", state=Done}, {message="bye bye", state=ToDo}]


I reduced the amount of code to paste that here but the error is the same.

$ elm --version
0.17.1

EDIT: code formatting and elm version
EDIT 2: I just realised I actually just hit the type-checker hole documented at #1214, sorry for posting that here!

@jvoigtlaender
Copy link
Contributor

@vrescobar, yours is a problem in your code, not in the compiler. Specifically, it has nothing to do with the compiler bug this GH issue here is about. The problem in your code comes from having

type TodoState = ToDo | Done
type alias TodoItem =
  { message: String
  , state: TodoState
  }

and then

item : TodoItem

and then trying to do

case item.itemState of
                    ToDo -> False
                    Done -> True

Why should this work? Your item has type TodoItem, and type TodoItem has no field itemState, so you should not expect item.itemState to work.

@vrescobar
Copy link

Ups, you are right, I am sorry. Somehow I messed up after a refactoring and I got blind with the Html a/Html b types.

@Erudition
Copy link

Is there a newer issue for this? I keep running into this problem with present-day Elm so it certainly hasn't been fixed. (or it regressed)

@mgold
Copy link
Contributor

mgold commented Jul 21, 2018

File a new issue with an sscce.

@Erudition
Copy link

@mgold I'm new to Elm, so I'm not sure what the problem could be, in order to isolate it.

@Janiczek
Copy link
Author

@Erudition The process is usually to remove stuff (record fields, functions, files, types, ...) until the issue disappears, then put that back in and remove everything else. (Surely an empty program doesn't have the issue, and your program does. By intermediate value theorem you are guaranteed to find the issue source sooner or later 😃 )

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

10 participants