Added strawman designs
This commit is contained in:
parent
77b1fab74f
commit
36a7b4dba3
54
strawman1.txt
Normal file
54
strawman1.txt
Normal file
@ -0,0 +1,54 @@
|
||||
# binary_tree takes a comparison function and returns a record (like a JS
|
||||
# object) as a module for handling binary trees of values of the same type
|
||||
# as compare's arguments.
|
||||
def binary_tree(compare) =
|
||||
# Backticks here indicate that leaf is a constructor for a tagged union
|
||||
# (variant) type.
|
||||
let empty = `leaf
|
||||
and insert(tree, value) = when tree is
|
||||
`leaf => `branch({left=`leaf, value = value, right=`leaf),
|
||||
`branch({left, value=value2, right) => when compare(value, value2) is
|
||||
`eq => `branch({left, value, right}),
|
||||
`lt =>
|
||||
let new_left = insert(left, value)
|
||||
in `branch({left=new_left, value=value2, right}),
|
||||
`gt =>
|
||||
let new_right = insert(right, value)
|
||||
in `branch({left, value=value2, new_right})
|
||||
and find(tree, needle) = when tree is
|
||||
`leaf => `nothing,
|
||||
`branch({left, value, right}) => when compare(needle, value) is
|
||||
`eq => `some(value),
|
||||
`lt => find(left, needle),
|
||||
`gt => find(right, needle),
|
||||
and func_we_dont_care_about() = print("ignore me")
|
||||
in {empty, insert, find}
|
||||
|
||||
# Prints "`some(2)".
|
||||
def do_tree_things() =
|
||||
# Assume that int_compare is in the stdlib or something.
|
||||
# Notice that we can ignore a record field. I want to make record
|
||||
# syntax basically the same as in JS.
|
||||
let {insert, empty, find, ...stuff_we_dont_need} = binary_tree(int_compare)
|
||||
# I invented this fancy partial-application syntax with _, and then found
|
||||
# that Scala does it the same way. Also note the pipe operator from Elm.
|
||||
and _ = empty |> insert(_, 5) |> insert(_, 2) |> insert(_, 10)
|
||||
|> find(2) |> print
|
||||
in stuff_we_dont_need
|
||||
|
||||
# Prints "`some(2)\nignore me".
|
||||
def main() = do_tree_things().func_we_dont_care_about()
|
||||
|
||||
|
||||
# I'd better be able to make type inference work, or else the user might have
|
||||
# to annotate binary_tree like this:
|
||||
binary_tree : fn('a, 'a) -> Record{
|
||||
empty: recurse T in Union{leaf, branch of Record{left: T, value: 'a, right: T},
|
||||
insert: fn(recurse T in Union{leaf, branch of Record{left: T, value: 'a, right: T}, 'a) ->
|
||||
recurse T in Union{leaf, branch of Record{left: T, value: 'a, right: T},
|
||||
find: fn(recurse T in Union{leaf, branch of Record{left: T, value: 'a, right: T}, 'a) ->
|
||||
Union{some of 'a, nothing},
|
||||
func_we_dont_care_about: fn() -> Record{}
|
||||
}
|
||||
# Though it might be possible to introduce a way to factor out the tree type,
|
||||
# which is the big thing starting with "recurse T".
|
63
strawman2.txt
Normal file
63
strawman2.txt
Normal file
@ -0,0 +1,63 @@
|
||||
# v2
|
||||
# I realized/remembered that I don't need `def`, `and`, or commas. Because function
|
||||
# application isn't by juxtapositon, I can look ahead by one token to know
|
||||
# whether the expression is complete.
|
||||
# If the language is impure, it also needs a sequencing operator. Let's use ;
|
||||
# because why not?
|
||||
|
||||
# binary_tree takes a comparison function and returns a record (like a JS
|
||||
# object) as a module for handling binary trees of values of the same type
|
||||
# as compare's arguments.
|
||||
binary_tree(compare) =
|
||||
# Backticks here indicate that leaf is a constructor for a tagged union
|
||||
# (variant) type.
|
||||
let
|
||||
empty = `leaf
|
||||
insert(tree, value) = when tree is
|
||||
`leaf => `branch({left=`leaf, value = value, right=`leaf)
|
||||
`branch({left, value=value2, right) => when compare(value, value2) is
|
||||
`eq => `branch({left, value, right})
|
||||
`lt =>
|
||||
let new_left = insert(left, value)
|
||||
in `branch({left=new_left, value=value2, right})
|
||||
`gt =>
|
||||
let new_right = insert(right, value)
|
||||
in `branch({left, value=value2, new_right})
|
||||
find(tree, needle) = when tree is
|
||||
`leaf => `nothing
|
||||
`branch({left, value, right}) => when compare(needle, value) is
|
||||
`eq => `some(value)
|
||||
`lt => find(left, needle)
|
||||
`gt => find(right, needle)
|
||||
func_we_dont_care_about() = print("ignore me")
|
||||
in {empty, insert, find}
|
||||
|
||||
# Prints "`some(2)".
|
||||
do_tree_things() =
|
||||
# Assume that int_compare is in the stdlib or something.
|
||||
# Notice that we can ignore a record field. I want to make record
|
||||
# syntax basically the same as in JS.
|
||||
let {insert, empty, find, ...stuff_we_dont_need} = binary_tree(int_compare)
|
||||
# I invented this fancy partial-application syntax with _, and then found
|
||||
# that Scala does it the same way. Also note the pipe operator from Elm.
|
||||
in
|
||||
empty |> insert(_, 5) |> insert(_, 2) |> insert(_, 10)
|
||||
|> find(2) |> print;
|
||||
stuff_we_dont_need
|
||||
|
||||
# Prints "`some(2)\nignore me".
|
||||
def main() = do_tree_things().func_we_dont_care_about()
|
||||
|
||||
|
||||
# I'd better be able to make type inference work, or else the user might have
|
||||
# to annotate binary_tree like this:
|
||||
binary_tree : fn('a, 'a) -> Record{
|
||||
empty: recurse T in Union{leaf, branch of Record{left: T, value: 'a, right: T},
|
||||
insert: fn(recurse T in Union{leaf, branch of Record{left: T, value: 'a, right: T}, 'a) ->
|
||||
recurse T in Union{leaf, branch of Record{left: T, value: 'a, right: T},
|
||||
find: fn(recurse T in Union{leaf, branch of Record{left: T, value: 'a, right: T}, 'a) ->
|
||||
Union{some of 'a, nothing},
|
||||
func_we_dont_care_about: fn() -> Record{}
|
||||
}
|
||||
# Though it might be possible to introduce a way to factor out the tree type,
|
||||
# which is the big thing starting with "recurse T".
|
91
strawman3.txt
Normal file
91
strawman3.txt
Normal file
@ -0,0 +1,91 @@
|
||||
#v3
|
||||
# Added type aliases.
|
||||
# Added some row variable examples.
|
||||
# Added array type
|
||||
|
||||
# TODO Add optional types
|
||||
# TODO Get rid of ambiguous syntax at end of type alias definition.
|
||||
# TODO Try out arbitrary strings as field names
|
||||
# This should be fine without any extra syntax, as long as we don't allow pattern-matching primitive values.
|
||||
# For consistency, especially re: punning, we'd need to also allow this for normal identifiers, which would need
|
||||
# new syntax.
|
||||
# TODO Modules (files) and opaque types
|
||||
# TODO More primitive types
|
||||
|
||||
# v2
|
||||
# I realized/remembered that I don't need `def`, `and`, or commas. Because function
|
||||
# application isn't by juxtapositon, I can look ahead by one token to know
|
||||
# whether the expression is complete.
|
||||
# If the language is impure, it also needs a sequencing operator. Let's use ;
|
||||
# because why not?
|
||||
|
||||
|
||||
type alias tree 'a = recurse 'T in <leaf, branch: {left: 'T, value: 'a, right: 'T}>
|
||||
type alias maybe 'a = <some: 'a, nothing>
|
||||
type alias unit = {}
|
||||
binary_tree : fn('a, 'a) -> {
|
||||
empty: tree 'a,
|
||||
insert: fn(tree 'a, 'a) -> tree 'a,
|
||||
find: fn(tree 'a, 'a) -> maybe 'a,
|
||||
func_we_dont_care_about: fn() -> unit
|
||||
}
|
||||
|
||||
# Type aliases should appear in compiler messages.
|
||||
# binary_tree takes a comparison function and returns a record (like a JS
|
||||
# object) as a module for handling binary trees of values of the same type
|
||||
# as compare's arguments.
|
||||
binary_tree(compare) =
|
||||
# Backticks here indicate that leaf is a constructor for a tagged union
|
||||
# (variant) type.
|
||||
let
|
||||
# A let expression can bind multiple names. Functions have access to all names within the expression,
|
||||
# while other values only have access to names bound before them.
|
||||
# This is to prevent nonsensical mutual recursion between non-function values.
|
||||
empty = `leaf
|
||||
insert(tree, value) = when tree is
|
||||
`leaf => `branch({left=`leaf, value = value, right=`leaf)
|
||||
`branch({left, value=value2, right}) => when compare(value, value2) is
|
||||
`eq => `branch({left, value, right})
|
||||
`lt =>
|
||||
let new_left = insert(left, value)
|
||||
in `branch({left=new_left, value=value2, right})
|
||||
`gt =>
|
||||
let new_right = insert(right, value)
|
||||
in `branch({left, value=value2, new_right})
|
||||
find(tree, needle) = when tree is
|
||||
`leaf => `nothing
|
||||
`branch({left, value, right}) => when compare(needle, value) is
|
||||
`eq => `some(value)
|
||||
`lt => find(left, needle)
|
||||
`gt => find(right, needle)
|
||||
func_we_dont_care_about() = print("ignore me")
|
||||
in {empty, insert, find}
|
||||
|
||||
# Prints "`some(2)".
|
||||
do_tree_things() =
|
||||
# Assume that int_compare is in the stdlib or something.
|
||||
# Notice that we can ignore a record field. I want to make record
|
||||
# syntax basically the same as in JS.
|
||||
let {insert, empty, find | stuff_we_dont_need} = binary_tree(int_compare)
|
||||
# I invented this fancy partial-application syntax with _, and then found
|
||||
# that Scala does it the same way. Also note the pipe operator from Elm.
|
||||
in
|
||||
empty |> insert(_, 5) |> insert(_, 2) |> insert(_, 10)
|
||||
|> find(2) |> print;
|
||||
stuff_we_dont_need
|
||||
|
||||
# Prints "`some(2)\nignore me".
|
||||
main() = do_tree_things().func_we_dont_care_about()
|
||||
|
||||
annotate: fn('a, {|'b}) -> {note: 'a | 'b}
|
||||
annotate(note, obj) = { note | obj }
|
||||
|
||||
deannotate: fn({note: 'a | 'b}) -> {|'b}
|
||||
deannotate(r) = let {note=_ | rest} = r in rest
|
||||
|
||||
reannotate: fn('a, {note: 'a | 'b}) -> {note: 'a | 'b}
|
||||
reannotate(note, obj) = {|obj with note=note} # or maybe field punning: {|obj with note}
|
||||
|
||||
map: fn(fn('a) -> 'b, ['a]) -> ['b]
|
||||
|
||||
doubled = let double(n) = 2 * n in map(double, [1,2,3,4,5])
|
90
strawman4.txt
Normal file
90
strawman4.txt
Normal file
@ -0,0 +1,90 @@
|
||||
# v4
|
||||
# Moved type annotations into bindings.
|
||||
# Added def keyword for top-level bindings.
|
||||
# Added and keyword to multiple-binding let-expressions.
|
||||
|
||||
# v3
|
||||
# Added type aliases.
|
||||
# Added some row variable examples.
|
||||
# Added array type
|
||||
|
||||
# TODO Add optional types
|
||||
# TODO Get rid of ambiguous syntax at end of type alias definition.
|
||||
# TODO Try out arbitrary strings as field names
|
||||
# This should be fine without any extra syntax, as long as we don't allow pattern-matching primitive values.
|
||||
# For consistency, especially re: punning, we'd need to also allow this for normal identifiers, which would need
|
||||
# new syntax.
|
||||
# TODO Modules (files) and opaque types
|
||||
# TODO More primitive types
|
||||
|
||||
# v2
|
||||
# I realized/remembered that I don't need `def`, `and`, or commas. Because function
|
||||
# application isn't by juxtapositon, I can look ahead by one token to know
|
||||
# whether the expression is complete.
|
||||
# If the language is impure, it also needs a sequencing operator. Let's use ;
|
||||
# because why not?
|
||||
|
||||
|
||||
type alias tree 'a = recurse 'T in <leaf, branch: {left: 'T, value: 'a, right: 'T}>
|
||||
type alias maybe 'a = <some: 'a, nothing>
|
||||
type alias unit = {}
|
||||
|
||||
# Type aliases should appear in compiler messages.
|
||||
# binary_tree takes a comparison function and returns a record (like a JS
|
||||
# object) as a module for handling binary trees of values of the same type
|
||||
# as compare's arguments.
|
||||
def binary_tree(compare: fn('a) -> 'a) -> {
|
||||
empty: tree 'a,
|
||||
insert: fn(tree 'a, 'a) -> tree 'a,
|
||||
find: fn(tree 'a, 'a) -> maybe 'a,
|
||||
func_we_dont_care_about: fn() -> unit
|
||||
} =
|
||||
# A let expression can bind multiple names. Functions have access to all names within the expression,
|
||||
# while other values only have access to names bound before them.
|
||||
# This is to prevent nonsensical mutual recursion between non-function values.
|
||||
# Backticks here indicate that leaf is a constructor for a tagged union (variant) type.
|
||||
let empty = `leaf
|
||||
and insert(tree, value) = when tree is
|
||||
`leaf => `branch({left=`leaf, value = value, right=`leaf)
|
||||
`branch({left, value=value2, right}) => when compare(value, value2) is
|
||||
`eq => `branch({left, value, right})
|
||||
`lt =>
|
||||
let new_left = insert(left, value)
|
||||
in `branch({left=new_left, value=value2, right})
|
||||
`gt =>
|
||||
let new_right = insert(right, value)
|
||||
in `branch({left, value=value2, new_right})
|
||||
and find(tree, needle) = when tree is
|
||||
`leaf => `nothing
|
||||
`branch({left, value, right}) => when compare(needle, value) is
|
||||
`eq => `some(value)
|
||||
`lt => find(left, needle)
|
||||
`gt => find(right, needle)
|
||||
and func_we_dont_care_about() = print("ignore me")
|
||||
in {empty, insert, find, func_we_dont_care_about}
|
||||
|
||||
# Prints "`some(2)".
|
||||
def do_tree_things() =
|
||||
# Assume that int_compare is in the stdlib or something.
|
||||
let {insert, empty, find | stuff_we_dont_need} = binary_tree(int_compare)
|
||||
# I invented this fancy partial-application syntax with _, and then found
|
||||
# that Scala does it the same way. Also note the pipe operator from Elm.
|
||||
in
|
||||
empty |> insert(_, 5) |> insert(_, 2) |> insert(_, 10)
|
||||
|> find(2) |> print;
|
||||
stuff_we_dont_need
|
||||
|
||||
# Prints "`some(2)\nignore me".
|
||||
def main() = do_tree_things().func_we_dont_care_about()
|
||||
|
||||
def annotate(note: 'a, obj: {|'b}) -> {note: 'a | 'b} =
|
||||
{ note | obj }
|
||||
|
||||
def deannotate(r: {note: 'a | 'b}) -> {|'b} = let {note=_ | rest} = r in rest
|
||||
|
||||
def reannotate(note: 'a, obj: {note: 'a | 'b}) -> {note: 'a | 'b} =
|
||||
{|obj with note=note} # or maybe field punning: {|obj with note}
|
||||
|
||||
def map(f: fn('a) -> 'b, xs: ['a]) -> ['b] = crash("TODO")
|
||||
|
||||
def doubled = let double(n) = 2 * n in map(double, [1,2,3,4,5])
|
Loading…
Reference in New Issue
Block a user