Skip to content

Beta reduction, better API, better codegen for lambdas, ...#5

Merged
mdgriffith merged 7 commits into
mdgriffith:mainfrom
miniBill:beta-reduce
Nov 6, 2021
Merged

Beta reduction, better API, better codegen for lambdas, ...#5
mdgriffith merged 7 commits into
mdgriffith:mainfrom
miniBill:beta-reduce

Conversation

@miniBill
Copy link
Copy Markdown
Contributor

@miniBill miniBill commented Nov 6, 2021

Consolidated my PR

Comment on lines +327 to +339
lambdaArgType =
fields
|> List.map (\( fieldName, _ ) -> ( fieldName, expressionType ))
|> Annotation.record

lambdaValue arg =
fields
|> List.map
(\( fieldName, _ ) ->
ElmGen.field (Elm.string fieldName) (Elm.get fieldName arg)
)
|> Elm.list
|> ElmGen.record
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be rewritten with a single List.map followed by a List.unzip iff wanted

Comment on lines +575 to +588
getArgumentUnpacker (freshCount + 1)
two
(Elm.apply value [ Elm.value varName ])

varName =
"lambdaArg" ++ String.fromInt freshCount
in
Elm.apply f [ ElmGen.pass ]
ElmGen.lambdaBetaReduced (Elm.string varName)
(typeToExpression one)
(\_ ->
Elm.lambdaBetaReduced varName
(typeToAnnotation one)
(\_ -> f)
)
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The critical insight (does it merit a comment?) is that you:

  1. lambda (beta reduced) with varName both in the generated binding and the code that the binding generates
  2. lambda with varName "outside" (after calling the unpacker for the inner type) but apply it "inside" (before calling the unpacker)

Comment thread src/Elm.elm
in
Compiler.Expression
{ expression =
betaReduce <|
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the only line that differs from lambda, so there is some redundant code in those two functions

Comment thread src/Elm.elm
Comment on lines +1211 to +1218
popLast : List (Node.Node a) -> Maybe ( List (Node.Node a), a )
popLast lst =
case List.reverse lst of
[] ->
Nothing

last :: initReverse ->
Just ( List.reverse initReverse, Compiler.denode last )
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This... can definitely be optimized! Is it worth optimizing? (the lists will be tiny 99.99% of the time)

Comment thread src/Elm.elm
Comment on lines +1272 to +1285
else
e

_ ->
e

_ ->
e

_ ->
e

_ ->
e
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really don't like this part, but I fear that squashing it would require being more eager in what we evaluate, which is costly

Comment thread src/Elm.elm
e

Just ( initApplicationArgs, lastApplicationArg ) ->
if extractLastArg lastApplicationArg == Just lastLambdaArg then
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This if is the same as the if above it, which makes my spider smell sense tingle

Comment thread src/Elm.elm
Comment on lines +1225 to +1231
Exp.RecordAccess argNode fieldNode ->
let
fieldName =
Compiler.denode fieldNode

arg =
Compiler.denode argNode
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if this denoding would be better or worse if done as pattern matching on the first line?

@mdgriffith mdgriffith merged commit 20a4ee7 into mdgriffith:main Nov 6, 2021
@miniBill miniBill deleted the beta-reduce branch November 7, 2021 01:58
dillonkearns added a commit to dillonkearns/elm-codegen that referenced this pull request Mar 31, 2026
…Record.

Found 2 new bugs (finding mdgriffith#5):
- Elm.functionReduced with record accessor (\r -> r.name) generates
  type annotation `r -> name` instead of `{ b | name : a } -> a`
- Elm.functionReduced with arithmetic (\x -> x + 1) generates
  type annotation `x -> Int` instead of `Int -> Int`

Both are betaReduce annotation bugs — the expression is correctly
reduced but the type annotation doesn't reflect the body's constraints.

Coverage improvements:
  TOTAL: 53.5% -> 57.4%
  Elm: 47.0% -> 57.6% (+10.6%)
  Internal.Compiler: 43.5% -> 48.9% (+5.4%)
  Internal.Clean: 84.6% -> 92.3%

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
dillonkearns added a commit to dillonkearns/elm-codegen that referenced this pull request Mar 31, 2026
…ors.

Found 1 new bug (finding mdgriffith#6):
- Elm.Arg.aliasAs generates fresh type variable for the alias instead
  of the actual record type from the underlying pattern.

Coverage improvements:
  TOTAL: 57.4% -> 60.9%
  Elm.Declare: 33.7% -> 54.2% (+20.5%)
  Elm.Arg: 55.6% -> 77.8% (+22.2%)
  Internal.Arg: 57.4% -> 69.8% (+12.4%)
  Internal.Write: 66.1% -> 71.0%

6 bugs total found by property testing:
  mdgriffith#1 Number type variable collision (FIXED)
  mdgriffith#2 Pipe + lambda parens (FIXED)
  mdgriffith#3 Operator multi-line indentation
  mdgriffith#4 Comparable type variable leak
  mdgriffith#5 functionReduced wrong type annotations
  mdgriffith#6 aliasAs wrong type for alias variable

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
dillonkearns added a commit to dillonkearns/elm-codegen that referenced this pull request Mar 31, 2026
Found 2 more bugs:
- Finding mdgriffith#5 update: Elm.unwrapper also generates wrong type
  annotations (val -> unwrapped instead of Wrapper -> Int)
- Finding mdgriffith#7: Elm.Op.pipeLeft with lambda absorbs <| into
  lambda body (\x -> x <| "hello" instead of (\x -> x) <| "hello")

Coverage: 60.9% -> 62.5%
  Elm: 60.5% -> 64.6%
  Elm.Declare: 54.2% -> 63.9% (+9.7%)
  Elm.Op: 75.6% -> 80.5%

Total bugs found: 7
  mdgriffith#1 Number type variable collision (FIXED)
  mdgriffith#2 Pipe + lambda parens (FIXED)
  mdgriffith#3 Operator multi-line indentation
  mdgriffith#4 Comparable type variable leak
  mdgriffith#5 functionReduced/unwrapper wrong type annotations
  mdgriffith#6 aliasAs wrong type for alias variable
  mdgriffith#7 pipeLeft lambda not parenthesized

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
dillonkearns added a commit to dillonkearns/elm-codegen that referenced this pull request Mar 31, 2026
Found 1 new class of bugs (finding mdgriffith#8):
- 19 missing type annotations across 6 different API patterns
  (Let.unpack, Let.fn, unwrap, Declare.value, Elm.get through alias)
- elm-codegen's type inference silently fails for these patterns,
  producing declarations without type annotations

Coverage: 62.5% -> 63.2%

Total bugs found: 8
  mdgriffith#1 Number type variable collision (FIXED)
  mdgriffith#2 Pipe + lambda parens (FIXED)
  mdgriffith#3 Operator multi-line indentation
  mdgriffith#4 Comparable type variable leak
  mdgriffith#5 functionReduced/unwrapper wrong type annotations
  mdgriffith#6 aliasAs wrong type for alias variable
  mdgriffith#7 pipeLeft lambda not parenthesized
  mdgriffith#8 Missing type annotations for multiple APIs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants