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

Extensible Record behavior does not match elm, throws run-time type errors #1094

Open
edwardpeters opened this issue Aug 18, 2023 · 0 comments
Labels
task Task level project item

Comments

@edwardpeters
Copy link

edwardpeters commented Aug 18, 2023

EDIT: I filed this bug based on my understanding of how extensible record types work in Elm; following conversations in finos/morphir#199 , it's clear those are not the expected semantics in Morphir. However, these still cause definite errors in Morphir - in particular, incomprehensible compilation failures and runtime type errors.

Describe the bug
Morphir-elm and Ellie seem to accept different things regarding Extensible Records. morphir-elm allows code to compile which throws type errors at run time (in morphir-elm develop, at least.)

To Reproduce

In Ellie, defining an alias to an extensible record requires a type parameter be provided on the alias:
type alias HasA e = {e | name : String }
If you omit the type parameter, Ellie will not compile:

type alias HasA = {e | name : String }
...
The `HasA` type alias uses an unbound type variable `e` in its definition:
8| type alias HasA = {e | name : String }

Morphir-elm accepts both cases (but I believe this causes problems later - see below)

Additionally, there are cases where the type parameter is used where morphir-elm breaks and Ellie works:

type alias HasA e = {e | a : String }

getA : HasA e -> String
getA rec = rec.a

foo : String -> String
foo s = 
        getA {name = "Bob", a = "A"}

Ellie runs this as expected, morphir-elm fails with:

/usr/local/lib/node_modules/morphir-elm/cli2/Morphir.Elm.CLI.js:726
var _Utils_compare = F2(function(x, y)
                                ^
RangeError: Maximum call stack size exceeded
    at Function.f (/usr/local/lib/node_modules/morphir-elm/cli2/Morphir.Elm.CLI.js:726:33)

However, if the type parameter is omitted on the function, morphir-elm works (but Ellie rejects):

type alias HasA e = {e | a : String }

getA : HasA -> String
getA rec = rec.a

foo : String -> String
foo s = 
        getA {name = "Bob", a = "A"}

This allows for run-time type errors, as from the following code:

type alias HasA e = {e | a : String }

makeList : HasA -> HasA -> List HasA
makeList a b = [a, b]

makeList2 : HasA -> HasA -> List HasA
makeList2 a b = [a, a]

works : String -> List HasA
works s = makeList2 {a = s, number = 5} {a = s, otherNumber = 5}

breaks : String -> List HasA
breaks s = makeList {a = s, number = 5} {a = s, otherNumber = 5}

In the above code, makeList and makeList2 both take two arguments of type HasA with no type parameter specified, and return a list (with the type parameter similarly not given. However, makeList uses them in such a way that their types should agree, while makeList2 allows them to be distinct. This is not apparent on the type level, but is up to the implementation of the function.

This code compiles in morphir-elm (Ellie rejects it due to unspecified type parameters.) In morphir-elm develop, works gives correct output, but breaks fails with a type error Output value: Could not unify '{ t9 = a : (), number : t6 }' with '{ t5 = a : (), otherNumber : t2 }' because the fields don't match

(The type error is correct, but I believe it should have been caught at compile time)

Expected behavior
I expected the morphir-elm behavior to match the elm behavior and:

  • Require type arguments to be specified, both in type aliases and function declarations
  • Type errors such as the one shown in the last example to be caught at compile time

Desktop (please complete the following information):

  • OS: OSX
  • Browser: Safari
  • Version: 2.89.0

Additional context
I'm working on a Scala-based type checker for Morphir IR, and am trying to nail down the space of what is "Legal" IR, especially with regard to generics/type variables.

@edwardpeters edwardpeters added the task Task level project item label Aug 18, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
task Task level project item
Projects
None yet
Development

No branches or pull requests

1 participant