Skip to content

Commit f963186

Browse files
authored
Merge pull request #137 from dillonkearns/fix/let-fn-annotation
Fix Elm.Let.fn/fn2/fn3 missing type annotations in declarations
2 parents de61886 + 987c7f4 commit f963186

2 files changed

Lines changed: 132 additions & 12 deletions

File tree

src/Elm/Let.elm

Lines changed: 66 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ import Elm exposing (Expression)
142142
import Elm.Syntax.Expression as Exp
143143
import Elm.Syntax.Node as Node
144144
import Elm.Syntax.Pattern as Pattern
145+
import Elm.Syntax.TypeAnnotation as Annotation
145146
import Internal.Arg
146147
import Internal.Compiler as Compiler exposing (Module)
147148
import Internal.Index as Index
@@ -335,10 +336,13 @@ fn desiredName arg toInnerFn sourceLet =
335336
Elm.apply
336337
(Compiler.Expression
337338
(\_ ->
338-
{ innerFnDetails
339-
| expression =
340-
Exp.FunctionOrValue []
341-
name
339+
{ expression =
340+
Exp.FunctionOrValue [] name
341+
, annotation =
342+
letFnAnnotation
343+
[ argDetails.details.annotation ]
344+
innerFnDetails.annotation
345+
, imports = innerFnDetails.imports
342346
}
343347
)
344348
)
@@ -349,6 +353,45 @@ fn desiredName arg toInnerFn sourceLet =
349353
)
350354

351355

356+
{-| Build the function type annotation for a let-bound function's
357+
reference expression. Takes the arg annotations (in order) and the
358+
body's annotation, and produces `arg1 -> arg2 -> ... -> body`.
359+
360+
This is needed because `Elm.apply` needs a proper function type
361+
annotation to derive the return type when calling the let-bound
362+
function. Without this, the call would use the body's annotation
363+
directly, which is the return type rather than a function type.
364+
-}
365+
letFnAnnotation :
366+
List (Result (List Compiler.InferenceError) Compiler.Inference)
367+
-> Result (List Compiler.InferenceError) Compiler.Inference
368+
-> Result (List Compiler.InferenceError) Compiler.Inference
369+
letFnAnnotation argAnnotations bodyAnnotation =
370+
List.foldr
371+
(\argResult resultSoFar ->
372+
Result.map2
373+
(\argAnn soFar ->
374+
{ type_ =
375+
Annotation.FunctionTypeAnnotation
376+
(Compiler.nodify argAnn.type_)
377+
(Compiler.nodify soFar.type_)
378+
, inferences =
379+
Compiler.mergeInferences
380+
argAnn.inferences
381+
soFar.inferences
382+
, aliases =
383+
Compiler.mergeAliases
384+
argAnn.aliases
385+
soFar.aliases
386+
}
387+
)
388+
argResult
389+
resultSoFar
390+
)
391+
bodyAnnotation
392+
argAnnotations
393+
394+
352395
{-| -}
353396
fn2 :
354397
String
@@ -399,10 +442,15 @@ fn2 desiredName argOne argTwo toInnerFn sourceLet =
399442
Elm.apply
400443
(Compiler.Expression
401444
(\_ ->
402-
{ innerFnDetails
403-
| expression =
404-
Exp.FunctionOrValue []
405-
name
445+
{ expression =
446+
Exp.FunctionOrValue [] name
447+
, annotation =
448+
letFnAnnotation
449+
[ argOneDetails.details.annotation
450+
, argTwoDetails.details.annotation
451+
]
452+
innerFnDetails.annotation
453+
, imports = innerFnDetails.imports
406454
}
407455
)
408456
)
@@ -473,10 +521,16 @@ fn3 desiredName argOne argTwo argThree toInnerFn sourceLet =
473521
Elm.apply
474522
(Compiler.Expression
475523
(\_ ->
476-
{ innerFnDetails
477-
| expression =
478-
Exp.FunctionOrValue []
479-
name
524+
{ expression =
525+
Exp.FunctionOrValue [] name
526+
, annotation =
527+
letFnAnnotation
528+
[ argOneDetails.details.annotation
529+
, argTwoDetails.details.annotation
530+
, argThreeDetails.details.annotation
531+
]
532+
innerFnDetails.annotation
533+
, imports = innerFnDetails.imports
480534
}
481535
)
482536
)

tests/TypeChecking.elm

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import Elm.Arg as Arg
66
import Elm.Case
77
import Elm.Declare
88
import Elm.Expect
9+
import Elm.Let
910
import Elm.Op
1011
import Elm.ToString
1112
import Expect
@@ -271,6 +272,71 @@ generatedCode =
271272
( 1 + 2, x )
272273
"""
273274
]
275+
, test "Elm.Let.fn declaration has a type annotation" <|
276+
\_ ->
277+
Elm.declaration "useLetFn"
278+
(Elm.Let.letIn
279+
(\myFn -> myFn (Elm.int 5))
280+
|> Elm.Let.fn "myFn"
281+
(Arg.var "x")
282+
(\x -> Elm.Op.plus x (Elm.int 1))
283+
|> Elm.Let.toExpression
284+
)
285+
|> Elm.Expect.declarationAs
286+
"""
287+
useLetFn : Int
288+
useLetFn =
289+
let
290+
myFn x =
291+
x + 1
292+
in
293+
myFn 5
294+
"""
295+
, test "Elm.Let.fn2 declaration has a type annotation" <|
296+
\_ ->
297+
Elm.declaration "useLetFn2"
298+
(Elm.Let.letIn
299+
(\myFn -> myFn (Elm.int 1) (Elm.int 2))
300+
|> Elm.Let.fn2 "myFn"
301+
(Arg.var "x")
302+
(Arg.var "y")
303+
(\x y -> Elm.Op.plus x y)
304+
|> Elm.Let.toExpression
305+
)
306+
|> Elm.Expect.declarationAs
307+
"""
308+
useLetFn2 : Int
309+
useLetFn2 =
310+
let
311+
myFn x y =
312+
x + y
313+
in
314+
myFn 1 2
315+
"""
316+
, test "Elm.Let.fn3 declaration has a type annotation" <|
317+
\_ ->
318+
Elm.declaration "useLetFn3"
319+
(Elm.Let.letIn
320+
(\myFn -> myFn (Elm.int 1) (Elm.int 2) (Elm.int 3))
321+
|> Elm.Let.fn3 "myFn"
322+
(Arg.var "x")
323+
(Arg.var "y")
324+
(Arg.var "z")
325+
(\x y z ->
326+
Elm.Op.plus x (Elm.Op.plus y z)
327+
)
328+
|> Elm.Let.toExpression
329+
)
330+
|> Elm.Expect.declarationAs
331+
"""
332+
useLetFn3 : Int
333+
useLetFn3 =
334+
let
335+
myFn x y z =
336+
x + (y + z)
337+
in
338+
myFn 1 2 3
339+
"""
274340
, describe "Typeclass constraints preserved in polymorphic annotations"
275341
[ test "number constraint: polymorphic plus produces number annotation" <|
276342
\_ ->

0 commit comments

Comments
 (0)