diff --git a/src/Elm/Let.elm b/src/Elm/Let.elm index 0df1431..b4c93b8 100644 --- a/src/Elm/Let.elm +++ b/src/Elm/Let.elm @@ -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 @@ -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 } ) ) @@ -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 @@ -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 } ) ) @@ -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 } ) ) diff --git a/tests/TypeChecking.elm b/tests/TypeChecking.elm index d3c5154..efca18c 100644 --- a/tests/TypeChecking.elm +++ b/tests/TypeChecking.elm @@ -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 @@ -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" <| \_ ->