Sébastien Besnier
@_sebbes_
Functional language
designed for front end development
Maintainer: Martin Janiczek @janiczek
Functional language
designed for front end development
Functional language
designed for front end development
Source code
Frontend
Canonical
Typed
Typed
Compiled program
Parse
Desugar
Infer types
Optimized
Emit
Source code
Frontend
Canonical
Typed
Typed
Compiled program
Parse
Desugar
Infer types
Optimized
Emit
x += 5
x = x + 5
x += 5
x = x + 5
Desugar
module Person exposing (..)
import Tree exposing (height)
name = "Sébastien"
c = name
+ " on " + Tree.name
+ " alt: " + height
module Tree exposing
(name, height)
name = "Christmas tree"
height = "4 meters"
module Person exposing (..)
import Tree exposing (height)
name = "Sébastien"
c = name
+ " on " + Tree.name
+ " alt: " + height
module Tree exposing
(name, height)
name = "Christmas tree"
height = "4 meters"
module Person exposing (..)
import Tree exposing (height)
name = "Sébastien"
c = name
+ " on " + Tree.name
+ " alt: " + height
module Tree exposing
(name, height)
name = "Christmas tree"
height = "4 meters"
{ moduleName = Nothing
, varName = "name"}
{ moduleName = Just "Tree"
, varName = "name"}
{ moduleName = Nothing
, varName = "height"}
Frontend
module Person exposing (..)
import Tree exposing (height)
name = "Sébastien"
c = name
+ " on " + Tree.name
+ " alt: " + height
module Tree exposing
(name, height)
name = "Christmas tree"
height = "4 meters"
{ moduleName = Nothing
, varName = "name"}
{ moduleName = Just "Tree"
, varName = "name"}
{ moduleName = Nothing
, varName = "height"}
Frontend
{ moduleName = "Person"
, varName = "name"}
{ moduleName = "Tree"
, varName = "name"}
{ moduleName = "Tree"
, varName = "height"}
Desugar
Canonical
...
, test "desugar variable name in module" <|
\_ ->
Desugar.desugarExpr Dict.empty moduleWithVarA varANotPrefixed
|> mapUnwrap
|> Expect.equal (Ok <| CanonicalU.Var
{ module_ = moduleWithVarA.name, name = "a" })
, test "desugar variable name NOT in this module" <|
\_ ->
Desugar.desugarExpr Dict.empty dummyModule varANotPrefixed
|> Expect.equal
(Err
(CompilerError.VarNameNotFound
{ insideModule = dummyModule.name
, var = { module_ = Nothing, name = "a" }
}
)
)
...
...
, test "desugar variable name in module" <|
\_ ->
Desugar.desugarExpr Dict.empty moduleWithVarA varANotPrefixed
|> mapUnwrap
|> Expect.equal (Ok <| CanonicalU.Var
{ module_ = moduleWithVarA.name, name = "a" })
, test "desugar variable name NOT in this module" <|
\_ ->
Desugar.desugarExpr Dict.empty dummyModule varANotPrefixed
|> Expect.equal
(Err
(CompilerError.VarNameNotFound
{ insideModule = dummyModule.name
, var = { module_ = Nothing, name = "a" }
}
)
)
...
First test
Second test
...
, test "desugar variable name in module" <|
\_ ->
Desugar.desugarExpr Dict.empty moduleWithVarA varANotPrefixed
|> mapUnwrap
|> Expect.equal (Ok <| CanonicalU.Var
{ module_ = moduleWithVarA.name, name = "a" })
, test "desugar variable name NOT in this module" <|
\_ ->
Desugar.desugarExpr Dict.empty dummyModule varANotPrefixed
|> Expect.equal
(Err
(CompilerError.VarNameNotFound
{ insideModule = dummyModule.name
, var = { module_ = Nothing, name = "a" }
}
)
)
...
Each new test asked for its own helper
toTest
{ description = "desugar variable name in module"
, thisModuleName = "A"
, thisModuleVars = [ "a" ]
, thisModuleImports = []
, availableModules = []
, inputVar = ( Nothing, "a" )
, expectedResult = Ok ( "A", "a" )
}
toTest
{ description = "desugar variable name in module"
, thisModuleName = "A"
, thisModuleVars = [ "a" ]
, thisModuleImports = []
, availableModules = []
, inputVar = ( Nothing, "a" )
, expectedResult = Ok ( "A", "a" )
}
toTest
{ description = "desugar prefixed variable name: import B ; B.a"
, thisModuleName = "A"
, thisModuleVars = [ "a" ]
, thisModuleImports = [ importFromName "B" ]
, availableModules = [ { name = "B", exposedVars = [ "a" ] } ]
, inputVar = ( Just "B", "a" )
, expectedResult = Ok ( "B", "a" )
}
toTest
{ description = "desugar variable name in module"
, thisModuleName = "A"
, thisModuleVars = [ "a" ]
, thisModuleImports = []
, availableModules = []
, inputVar = ( Nothing, "a" )
, expectedResult = Ok ( "A", "a" )
}
toTest
{ description = "desugar prefixed variable name: import B ; B.a"
, thisModuleName = "A"
, thisModuleVars = [ "a" ]
, thisModuleImports = [ importFromName "B" ]
, availableModules = [ { name = "B", exposedVars = [ "a" ] } ]
, inputVar = ( Just "B", "a" )
, expectedResult = Ok ( "B", "a" )
}
toTest
{ description = "desugar variable name NOT in this module"
, thisModuleName = "A"
, thisModuleVars = [ "a" ]
, thisModuleImports = []
, availableModules = []
, inputVar = ( Nothing, "b" )
, expectedResult =
Err
(CompilerError.VarNameNotFound
{ insideModule = "A"
, var = { module_ = Nothing, name = "b" }
}
)
}
\x y -> x + y
Anonymous function in elm
(x, y) => x+y
JS (ES6) version
function(x, y){ return x+y;}
JS (before ES6) version
\x y -> x + y
\x -> (\y -> x + y)
is the same as
\x y -> x + y
\x -> (\y -> x + y)
is the same as
1 function with 2 arguments
2 functions with 1 argument each
Source code
Frontend
Parse
"\x y -> x + y"
String
Source code
Frontend
Parse
"\x y -> x + y"
String
{ arguments =
["x", "y"]
, body = Plus
(Var "x")
(Var "y")
}
\x y -> x + y
Source code
Frontend
Parse
"\x y -> x + y"
String
{ arguments =
["x", "y"]
, body = Plus
(Var "x")
(Var "y")
}
\x y -> x + y
Frontend
Canonical
{ arguments =
["x", "y"]
, body = Plus
(Var "x")
(Var "y")
}
\x y -> x + y
\x -> (\y -> x + y)
Desugar
Frontend
Canonical
{ arguments =
["x", "y"]
, body = Plus
(Var "x")
(Var "y")
}
{ argument = "x"
, body = {
{ argument = "y"
, body = Plus
(Var "x")
(Var "y")
}
}
\x y -> x + y
\x -> (\y -> x + y)
Desugar
Frontend
Canonical
{ arguments =
["x", "y"]
, body = Plus
(Var "x")
(Var "y")
}
{ argument = "x"
, body = {
{ argument = "y"
, body = Plus
(Var "x")
(Var "y")
}
}
\x y -> x + y
\x -> (\y -> x + y)
Desugar
Frontend
Canonical
{ arguments =
["x", "y"]
, body = Plus
(Var "x")
(Var "y")
}
{ argument = "x"
, body = {
{ argument = "y"
, body = Plus
(Var "x")
(Var "y")
}
}
\x y -> x + y
\x -> (\y -> x + y)
Desugar
Frontend
Canonical
{ arguments =
["x", "y"]
, body = Plus
(Var "x")
(Var "y")
}
{ argument = "x"
, body = {
{ argument = "y"
, body = Plus
(Var "x")
(Var "y")
}
}
\x y -> x + y
\x -> (\y -> x + y)
Desugar
I need to understand:
I need to understand:
good first issue
user =
{ name = "Seb"
, score = 42
, city = "Paris"
}
user =
{ name = "Seb"
, score = 42
, city = "Paris"
}
Binding
user =
{ name = "Seb"
, score = 42
, city = "Paris"
}
Binding
a =
let b = 42 in
52 + b
user =
{ name = "Seb"
, score = 42
, city = "Paris"
}
Binding
a =
let b = 42 in
52 + b
Binding
Mistakes
Avoid
( x + y, zz )
1
( x + y, zz )
1
2
( x + y, zz )
1
2
3
( x + y, zz )
1
2
3
4
( x + y, zz )
1
2
3
4
5
( x + y, zz )
But all values are immutable in elm!
assignId currentId typedExpr =
((typedExpr, Var currentId), currentId + 1)
assignId currentId typedExpr =
((typedExpr, Var currentId), currentId + 1)
Numbered value
New counter
( x + y, zz )
assignIdsWith currentId expr =
case expr of
Canonical.Var name ->
assignId currentId (Typed.Var name)
...
assignId currentId typedExpr =
((typedExpr, Var currentId), currentId + 1)
2
( x + y, zz )
2
assignIdsWith currentId expr =
case expr of
Canonical.Var name ->
assignId currentId (Typed.Var name)
...
assignId currentId typedExpr =
((typedExpr, Var currentId), currentId + 1)
( x + y, zz )
1
2
3
assignIdsWith currentId expr =
case expr of
Canonical.Var name ->
assignId currentId (Typed.Var name)
Canonical.Plus e1 e2 ->
let
( e1_, id1 ) =
assignIdsWith currentId e1
( e2_, id2 ) =
assignIdsWith id1 e2
in
assignId id2 (Typed.Plus e1_ e2_)
...
( x + y, zz )
1
2
3
assignIdsWith currentId expr =
case expr of
Canonical.Var name ->
assignId currentId (Typed.Var name)
Canonical.Plus e1 e2 ->
let
( e1_, id1 ) =
assignIdsWith currentId e1
( e2_, id2 ) =
assignIdsWith id1 e2
in
assignId id2 (Typed.Plus e1_ e2_)
...
( x + y, zz )
1
2
3
assignIdsWith currentId expr =
case expr of
Canonical.Var name ->
assignId currentId (Typed.Var name)
Canonical.Plus e1 e2 ->
let
( e1_, id1 ) =
assignIdsWith currentId e1
( e2_, id2 ) =
assignIdsWith id1 e2
in
assignId id2 (Typed.Plus e1_ e2_)
...
( x + y, zz )
1
2
3
assignIdsWith currentId expr =
case expr of
Canonical.Var name ->
assignId currentId (Typed.Var name)
Canonical.Plus e1 e2 ->
let
( e1_, id1 ) =
assignIdsWith currentId e1
( e2_, id2 ) =
assignIdsWith id1 e2
in
assignId id2 (Typed.Plus e1_ e2_)
...
( x + y, zz )
4
assignIdsWith currentId expr =
case expr of
Canonical.Var name ->
assignId currentId (Typed.Var name)
Canonical.Plus e1 e2 ->
let
( e1_, id1 ) =
assignIdsWith currentId e1
( e2_, id2 ) =
assignIdsWith id1 e2
in
assignId id2 (Typed.Plus e1_ e2_)
Canonical.Tuple e1 e2 ->
let
( e1_, id1 ) =
assignIdsWith currentId e1
( e2_, id2 ) =
assignIdsWith id1 e2
in
assignId id2 (Typed.Tuple e1_ e2_)
...
5
assignIdsWith currentId expr =
case expr of
...
Canonical.Record bindings ->
let
bindingsList =
Dict.toList bindings
( bindingBodiesList, newId ) =
List.foldl
(\( name, binding ) ( acc, runningId ) ->
let
( body__, nextId ) =
assignIdsWith runningId binding.body
newElt =
( name, { name = name, body = body__ } )
in
( newElt :: acc, nextId )
)
( [], currentId )
bindingsList
in
assignId currentId <|
Typed.Record (Dict.fromList bindingBodiesList)
assignIdsWith currentId expr =
case expr of
...
Canonical.Record bindings ->
let
bindingsList =
Dict.toList bindings
( bindingBodiesList, newId ) =
List.foldl
(\( name, binding ) ( acc, runningId ) ->
let
( body__, nextId ) =
assignIdsWith runningId binding.body
newElt =
( name, { name = name, body = body__ } )
in
( newElt :: acc, nextId )
)
( [], currentId )
bindingsList
in
assignId currentId <|
Typed.Record (Dict.fromList bindingBodiesList)
assignIdsWith currentId expr =
case expr of
...
Canonical.Record bindings ->
let
bindingsList =
Dict.toList bindings
( bindingBodiesList, newId ) =
List.foldl
(\( name, binding ) ( acc, runningId ) ->
let
( body__, nextId ) =
assignIdsWith runningId binding.body
newElt =
( name, { name = name, body = body__ } )
in
( newElt :: acc, nextId )
)
( [], currentId )
bindingsList
in
assignId currentId <|
Typed.Record (Dict.fromList bindingBodiesList)
unused variable newId
assignIdsWith currentId expr =
case expr of
...
Canonical.Record bindings ->
let
bindingsList =
Dict.toList bindings
( bindingBodiesList, newId ) =
List.foldl
(\( name, binding ) ( acc, runningId ) ->
let
( body__, nextId ) =
assignIdsWith runningId binding.body
newElt =
( name, { name = name, body = body__ } )
in
( newElt :: acc, nextId )
)
( [], currentId )
bindingsList
in
assignId currentId <|
Typed.Record (Dict.fromList bindingBodiesList)
unused variable newId
assignIdsWith currentId expr =
case expr of
...
Canonical.Record bindings ->
let
bindingsList =
Dict.toList bindings
( bindingBodiesList, newId ) =
List.foldl
(\( name, binding ) ( acc, runningId ) ->
let
( body__, nextId ) =
assignIdsWith runningId binding.body
newElt =
( name, { name = name, body = body__ } )
in
( newElt :: acc, nextId )
)
( [], currentId )
bindingsList
in
assignId newId <|
Typed.Record (Dict.fromList bindingBodiesList)
A tool that allows you to analyse your Elm code, identify deficiencies and apply best practices.
A tool that allows you to analyse your Elm code, identify deficiencies and apply best practices.
Source code
Frontend
Canonical
Typed
Typed
Compiled program
Parse
Desugar
Infer types
Optimized
Emit
{ pseudo = "Seb"
, age = 42
}
Record
[ { name = "pseudo"
, body = String "Seb"
}
, { name = "age"
, body = Int 42
}
]
Source code
Frontend
Parse
expr =
PP.expression
{ oneOf =
[ if_
, let_
, lambda
, PP.literal literal
, always var
, unit
, list
, tuple
, tuple3
, parenthesizedExpr
]
, ...
}
expr =
PP.expression
{ oneOf =
[ if_
, let_
, lambda
, PP.literal literal
, always var
, unit
, list
, tuple
, tuple3
, parenthesizedExpr
, record
]
, ...
}
record config =
P.succeed Frontend.Record
|= P.sequence
{ start = P.Token "{" ExpectingRecordLeftBrace
, separator = P.Token "," ExpectingRecordSeparator
, end = P.Token "}" ExpectingRecordRightBrace
, spaces = spacesOnly
, item = binding config
, trailing = P.Forbidden
}
|> P.inContext InRecordRecord
record config =
P.succeed Frontend.Record
|= P.sequence
{ start = P.Token "{" ExpectingRecordLeftBrace
, separator = P.Token "," ExpectingRecordSeparator
, end = P.Token "}" ExpectingRecordRightBrace
, spaces = spacesOnly
, item = binding config
, trailing = P.Forbidden
}
|> P.inContext InRecordRecord
On success, will build a Frontend.Record
record config =
P.succeed Frontend.Record
|= P.sequence
{ start = P.Token "{" ExpectingRecordLeftBrace
, separator = P.Token "," ExpectingRecordSeparator
, end = P.Token "}" ExpectingRecordRightBrace
, spaces = spacesOnly
, item = binding config
, trailing = P.Forbidden
}
|> P.inContext InRecordRecord
On success, will build a Frontend.Record
record config =
P.succeed Frontend.Record
|= P.sequence
{ start = P.Token "{" ExpectingRecordLeftBrace
, separator = P.Token "," ExpectingRecordSeparator
, end = P.Token "}" ExpectingRecordRightBrace
, spaces = spacesOnly
, item = binding config
, trailing = P.Forbidden
}
|> P.inContext InRecordRecord
On success, will build a Frontend.Record
type ParseProblem
= ...
| ExpectingLet
| ExpectingIn
| ExpectingUnit
| ExpectingRecordLeftBrace
| ExpectingRecordSeparator
| ExpectingRecordRightBrace
| InvalidNumber
...
record config =
P.succeed Frontend.Record
|= P.sequence
{ start = P.Token "{" ExpectingRecordLeftBrace
, separator = P.Token "," ExpectingRecordSeparator
, end = P.Token "}" ExpectingRecordRightBrace
, spaces = spacesOnly
, item = binding config
, trailing = P.Forbidden
}
|> P.inContext InRecordRecord
On success, will build a Frontend.Record
record config =
P.succeed Frontend.Record
|= P.sequence
{ start = P.Token "{" ExpectingRecordLeftBrace
, separator = P.Token "," ExpectingRecordSeparator
, end = P.Token "}" ExpectingRecordRightBrace
, spaces = spacesOnly
, item = binding config
, trailing = P.Forbidden
}
|> P.inContext InRecordRecord
On success, will build a Frontend.Record
binding config =
P.succeed Binding
|= varName
|. P.spaces
|. P.symbol (P.Token "=" ExpectingEqualsSign)
|. P.spaces
|= PP.subExpression 0 config
|> P.inContext InLetBinding
record config =
P.succeed Frontend.Record
|= P.sequence
{ start = P.Token "{" ExpectingRecordLeftBrace
, separator = P.Token "," ExpectingRecordSeparator
, end = P.Token "}" ExpectingRecordRightBrace
, spaces = spacesOnly
, item = binding config
, trailing = P.Forbidden
}
|> P.inContext InRecordRecord
On success, will build a Frontend.Record
binding config =
P.succeed Binding
|= varName
|. P.spaces
|. P.symbol (P.Token "=" ExpectingEqualsSign)
|. P.spaces
|= PP.subExpression 0 config
|> P.inContext InLetBinding
Parse and keep!
Parse and keep!
record config =
P.succeed Frontend.Record
|= P.sequence
{ start = P.Token "{" ExpectingRecordLeftBrace
, separator = P.Token "," ExpectingRecordSeparator
, end = P.Token "}" ExpectingRecordRightBrace
, spaces = spacesOnly
, item = binding config
, trailing = P.Forbidden
}
|> P.inContext InRecordRecord
On success, will build a Frontend.Record
binding config =
P.succeed Binding
|= varName
|. P.spaces
|. P.symbol (P.Token "=" ExpectingEqualsSign)
|. P.spaces
|= PP.subExpression 0 config
|> P.inContext InLetBinding
Parse and keep!
Parse and keep!
Parse and forget!
(case insensitive mode)
[A-Z0-9-_+].+@[A-Z0-9.-]+\.[A-Z]{2,}
[A-Z0-9-_+.]+@[A-Z0-9.-]+.[A-Z]{2,}
[A-Z0-9-_+.]+@[A-Z0-9.-]+\.[A-Z]{2,}
[A-Z0-9-_+.]+@[A-Z0-9.-+]\.[A-Z]{2,}
[A-Z0-9-_+].+@[A-Z0-9.-]+\.[A-Z]{2,}
[A-Z0-9-_+.]+@[A-Z0-9.-]+.[A-Z]{2,}
[A-Z0-9-_+.]+@[A-Z0-9.-]+\.[A-Z]{2,}
[A-Z0-9-_+.]+@[A-Z0-9.-+]\.[A-Z]{2,}
should be after the ]
should be escaped
should be in the [ ]
Cannot be recognized with a regex
(consequence of the "Pumping lemma")
Complex (nested) request!
module Main exposing (main)
main =
{ x = 42, y = "Seb" }
module Main exposing (main)
main =
{ x = 42, y = "Seb" }
$ make
Compilation finished, writing output to `out.js`.
---------------------------
-- WRITING TO FS ----------
---------------------------
const Main$main = {x : 42, y : "Seb"};
module Main exposing (main)
main =
{ x = 42, y = "Seb" }
$ make
Compilation finished, writing output to `out.js`.
---------------------------
-- WRITING TO FS ----------
---------------------------
const Main$main = {x : 42, y : "Seb"};
fuzz string "reverse twice is identity" <|
\randomlyGeneratedString ->
randomlyGeneratedString
|> String.reverse
|> String.reverse
|> Expect.equal randomlyGeneratedString
Source code
Frontend
Canonical
Typed
Typed
Compiled program
Parse
Infer types
Optimized
Emit
Desugar
type Type
= {- READ THIS!
When adding a case that recurs on Type, you'll have to add a case to
`InferTypes.Unify.unify`:
| MyNewType Type Type
will have to get a case:
(MyNewType m1e1 m1e2, MyNewType m2e1 m2e2) ->
substitutionMap
|> unify m1e1 m2e1
|> Result.andThen (unify m1e2 m2e2)
-}
Var Int
| Function Type Type
| Int
| Float
| Char
| String
...
unify t1 t2 substitutionMap =
case ( t1, t2 ) of
...
( List list1, List list2 ) ->
unify list1 list2 substitutionMap
( Tuple t1e1 t1e2, Tuple t2e1 t2e2 ) ->
substitutionMap
|> unify t1e1 t2e1
|> Result.andThen (unify t1e2 t2e2)
...
_ ->
Err ( TypeMismatch t1 t2, substitutionMap )
unify t1 t2 substitutionMap =
case ( t1, t2 ) of
...
( List list1, List list2 ) ->
unify list1 list2 substitutionMap
( Tuple t1e1 t1e2, Tuple t2e1 t2e2 ) ->
substitutionMap
|> unify t1e1 t2e1
|> Result.andThen (unify t1e2 t2e2)
( Record bindings1, Record bindings2 ) ->
...
...
_ ->
Err ( TypeMismatch t1 t2, substitutionMap )
Mistakes
Avoid
unify t1 t2 substitutionMap =
case ( t1, t2 ) of
...
( List list1, List list2 ) ->
unify list1 list2 substitutionMap
( Tuple t1e1 t1e2, Tuple t2e1 t2e2 ) ->
substitutionMap
|> unify t1e1 t2e1
|> Result.andThen (unify t1e2 t2e2)
...
_ ->
Err ( TypeMismatch t1 t2, substitutionMap )
And growing very fast!
unify t1 t2 substitutionMap =
case ( t1, t2 ) of
...
( List list1, List list2 ) ->
unify list1 list2 substitutionMap
( List _, _ ) ->
Err (TypeMismatch t1 t2, substitutionMap)
( Tuple t1e1 t1e2, Tuple t2e1 t2e2 ) ->
substitutionMap
|> unify t1e1 t2e1
|> Result.andThen (unify t1e2 t2e2)
( Tuple _, _ ) ->
Err (TypeMismatch t1 t2, substitutionMap)
...
_ ->
Err ( TypeMismatch t1 t2, substitutionMap )
unify t1 t2 substitutionMap =
case ( t1, t2 ) of
...
( List list1, List list2 ) ->
unify list1 list2 substitutionMap
( List _, _ ) ->
Err (TypeMismatch t1 t2, substitutionMap)
( Tuple t1e1 t1e2, Tuple t2e1 t2e2 ) ->
substitutionMap
|> unify t1e1 t2e1
|> Result.andThen (unify t1e2 t2e2)
( Tuple _, _ ) ->
Err (TypeMismatch t1 t2, substitutionMap)
...
_ ->
Err ( TypeMismatch t1 t2, substitutionMap )
add 2 cases when adding a type
Source code
Frontend
Canonical
Typed
Typed
Compiled program
Parse
Desugar
Infer types
Optimized
Emit
f (x + y) == "hello"
f (x + y)
f (x + y)
x and y has to be Ints
f (x + y)
x and y has to be Ints
Int
f (x + y)
x and y has to be Ints
Int
f : Int → ??
f (x + y)
x and y has to be Ints
Int
f : Int → ??
??
f (x + y) == "hello"
x and y has to be Ints
Int
f : Int → ??
??
f (x + y) == "hello"
x and y has to be Ints
Int
f : Int → ??
String
??
f (x + y) == "hello"
x and y has to be Ints
Int
f : Int → ??
String
??
?? has to be String
the result is a Bool
f (x + y) == "hello"
x and y has to be Ints
Int
f : Int → String
String
String
?? has to be String
the result is a Bool
f : Int -> String
x : Int
y : Int
Canonical
Typed
Infer types
Canonical
Typed
Infer types
Numbering
Generate equations
Unify
(solve the equations)
Canonical
Typed
Numbering
Generate equations
Unify
f (x + y) == "hello"
1
2
3
Canonical
Typed
Numbering
Generate equations
Unify
f (x + y) == "hello"
1
2
3
1 == Int 2 == Int 3 == Int
Canonical
Typed
Numbering
Generate equations
Unify
f (x + y) == "hello"
1
2
3
1 == Int 2 == Int 3 == Int
Substitution Map
1 : Int 2 : Int 3 : Int
Canonical
Typed
Numbering
Generate equations
Unify
f (x + y) == "hello"
1
2
3
4
5
1 == Int 2 == Int 3 == Int
Substitution Map
1 : Int 2 : Int 3 : Int
Canonical
Typed
Numbering
Generate equations
Unify
f (x + y) == "hello"
1
2
3
4
5
1 == Int 2 == Int 3 == Int
4 == 3 -> 5
Substitution Map
1 : Int 2 : Int 3 : Int
Canonical
Typed
Numbering
Generate equations
Unify
f (x + y) == "hello"
1
2
3
4
5
1 == Int 2 == Int 3 == Int
4 == 3 -> 5
Substitution Map
1 : Int 2 : Int 3 : Int
4 : 3 -> 5
Canonical
Typed
Numbering
Generate equations
Unify
f (x + y) == "hello"
1
2
3
4
5
1 == Int 2 == Int 3 == Int
4 == 3 -> 5
Substitution Map
1 : Int 2 : Int 3 : Int
4 : Int -> 5
Canonical
Typed
Numbering
Generate equations
Unify
f (x + y) == "hello"
1
2
3
4
5
6
1 == Int 2 == Int 3 == Int
4 == 3 -> 5
Substitution Map
1 : Int 2 : Int 3 : Int
4 : Int -> 5
Canonical
Typed
Numbering
Generate equations
Unify
f (x + y) == "hello"
1
2
3
4
5
6
1 == Int 2 == Int 3 == Int
4 == 3 -> 5
6 == String
Substitution Map
1 : Int 2 : Int 3 : Int
4 : Int -> 5
6 : String
Canonical
Typed
Numbering
Generate equations
Unify
f (x + y) == "hello"
1
2
3
4
5
6
7
1 == Int 2 == Int 3 == Int
4 == 3 -> 5
6 == String
Substitution Map
1 : Int 2 : Int 3 : Int
4 : Int -> 5
6 : String
Canonical
Typed
Numbering
Generate equations
Unify
f (x + y) == "hello"
1
2
3
4
5
6
7
1 == Int 2 == Int 3 == Int
4 == 3 -> 5
6 == String
5 == 6 7 == Bool
Substitution Map
1 : Int 2 : Int 3 : Int
4 : Int -> 5
6 : String
Canonical
Typed
Numbering
Generate equations
Unify
f (x + y) == "hello"
1
2
3
4
5
6
7
1 == Int 2 == Int 3 == Int
4 == 3 -> 5
6 == String
5 == 6 7 == Bool
Substitution Map
1 : Int 2 : Int 3 : Int
4 : Int -> String
6 : String
7 : Bool
Did not check for multiple fields of the same name
Did not check for multiple fields of the same name
{ name = "Sébastien"
, age = 42
, name = "Louis Auguste"
}
Mistakes
Avoid
Functional language
designed for front end development
and complex software
Sébastien Besnier
@_sebbes_