Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 66 additions & 12 deletions src/Elm/Let.elm
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ import Elm exposing (Expression)
import Elm.Syntax.Expression as Exp
import Elm.Syntax.Node as Node
import Elm.Syntax.Pattern as Pattern
import Elm.Syntax.TypeAnnotation as Annotation
import Internal.Arg
import Internal.Compiler as Compiler exposing (Module)
import Internal.Index as Index
Expand Down Expand Up @@ -335,10 +336,13 @@ fn desiredName arg toInnerFn sourceLet =
Elm.apply
(Compiler.Expression
(\_ ->
{ innerFnDetails
| expression =
Exp.FunctionOrValue []
name
{ expression =
Exp.FunctionOrValue [] name
, annotation =
letFnAnnotation
[ argDetails.details.annotation ]
innerFnDetails.annotation
, imports = innerFnDetails.imports
}
)
)
Expand All @@ -349,6 +353,45 @@ fn desiredName arg toInnerFn sourceLet =
)


{-| Build the function type annotation for a let-bound function's
reference expression. Takes the arg annotations (in order) and the
body's annotation, and produces `arg1 -> arg2 -> ... -> body`.

This is needed because `Elm.apply` needs a proper function type
annotation to derive the return type when calling the let-bound
function. Without this, the call would use the body's annotation
directly, which is the return type rather than a function type.
-}
letFnAnnotation :
List (Result (List Compiler.InferenceError) Compiler.Inference)
-> Result (List Compiler.InferenceError) Compiler.Inference
-> Result (List Compiler.InferenceError) Compiler.Inference
letFnAnnotation argAnnotations bodyAnnotation =
List.foldr
(\argResult resultSoFar ->
Result.map2
(\argAnn soFar ->
{ type_ =
Annotation.FunctionTypeAnnotation
(Compiler.nodify argAnn.type_)
(Compiler.nodify soFar.type_)
, inferences =
Compiler.mergeInferences
argAnn.inferences
soFar.inferences
, aliases =
Compiler.mergeAliases
argAnn.aliases
soFar.aliases
}
)
argResult
resultSoFar
)
bodyAnnotation
argAnnotations


{-| -}
fn2 :
String
Expand Down Expand Up @@ -399,10 +442,15 @@ fn2 desiredName argOne argTwo toInnerFn sourceLet =
Elm.apply
(Compiler.Expression
(\_ ->
{ innerFnDetails
| expression =
Exp.FunctionOrValue []
name
{ expression =
Exp.FunctionOrValue [] name
, annotation =
letFnAnnotation
[ argOneDetails.details.annotation
, argTwoDetails.details.annotation
]
innerFnDetails.annotation
, imports = innerFnDetails.imports
}
)
)
Expand Down Expand Up @@ -473,10 +521,16 @@ fn3 desiredName argOne argTwo argThree toInnerFn sourceLet =
Elm.apply
(Compiler.Expression
(\_ ->
{ innerFnDetails
| expression =
Exp.FunctionOrValue []
name
{ expression =
Exp.FunctionOrValue [] name
, annotation =
letFnAnnotation
[ argOneDetails.details.annotation
, argTwoDetails.details.annotation
, argThreeDetails.details.annotation
]
innerFnDetails.annotation
, imports = innerFnDetails.imports
}
)
)
Expand Down
66 changes: 66 additions & 0 deletions tests/TypeChecking.elm
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import Elm.Arg as Arg
import Elm.Case
import Elm.Declare
import Elm.Expect
import Elm.Let
import Elm.Op
import Elm.ToString
import Expect
Expand Down Expand Up @@ -271,6 +272,71 @@ generatedCode =
( 1 + 2, x )
"""
]
, test "Elm.Let.fn declaration has a type annotation" <|
\_ ->
Elm.declaration "useLetFn"
(Elm.Let.letIn
(\myFn -> myFn (Elm.int 5))
|> Elm.Let.fn "myFn"
(Arg.var "x")
(\x -> Elm.Op.plus x (Elm.int 1))
|> Elm.Let.toExpression
)
|> Elm.Expect.declarationAs
"""
useLetFn : Int
useLetFn =
let
myFn x =
x + 1
in
myFn 5
"""
, test "Elm.Let.fn2 declaration has a type annotation" <|
\_ ->
Elm.declaration "useLetFn2"
(Elm.Let.letIn
(\myFn -> myFn (Elm.int 1) (Elm.int 2))
|> Elm.Let.fn2 "myFn"
(Arg.var "x")
(Arg.var "y")
(\x y -> Elm.Op.plus x y)
|> Elm.Let.toExpression
)
|> Elm.Expect.declarationAs
"""
useLetFn2 : Int
useLetFn2 =
let
myFn x y =
x + y
in
myFn 1 2
"""
, test "Elm.Let.fn3 declaration has a type annotation" <|
\_ ->
Elm.declaration "useLetFn3"
(Elm.Let.letIn
(\myFn -> myFn (Elm.int 1) (Elm.int 2) (Elm.int 3))
|> Elm.Let.fn3 "myFn"
(Arg.var "x")
(Arg.var "y")
(Arg.var "z")
(\x y z ->
Elm.Op.plus x (Elm.Op.plus y z)
)
|> Elm.Let.toExpression
)
|> Elm.Expect.declarationAs
"""
useLetFn3 : Int
useLetFn3 =
let
myFn x y z =
x + (y + z)
in
myFn 1 2 3
"""
, describe "Typeclass constraints preserved in polymorphic annotations"
[ test "number constraint: polymorphic plus produces number annotation" <|
\_ ->
Expand Down
Loading