-
-
Save brecert/66a9691e77012af5e002b0c537e0274a to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| [- | |
| where | means to use this bind or that bind if the bind doesn't work | |
| -] | |
| ( IO ) : import { std.lib.io } | |
| question : ( IO ) [ prompt: String ] { IO.gets "{prompt}: " } | |
| question : ( IO ) [ prompt: String, valid: List[String] ] { | |
| input = IO.gets "{prompt}\n({valid.join ", "}): " .trim | |
| match { valid.contains input } { | |
| | ( input : True ) { input } | |
| | ( input ) { | |
| IO.puts "\"{input}\" is not a valid answer" | |
| question [ prompt, valid ] | |
| } | |
| } | |
| } | |
| main : ( IO ) { | |
| question [ "foo", List["bar", "baz"] ] | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| [- | |
| I've been thinking about programming languages again recently, came up with an idea that uses holes (I'm unsure if this is the formal definition of them or not, it's just a word I'm using to describe them) | |
| Please note the syntax and backing datatypes for a lot of this isn't too impactful, so when you see `LinkedList int`, just assume that's `LinkedList` with an `int` parameter, or when you see a `match` and don't worry about too much, just assume it does what you expect it to. | |
| where expressions are lazy by default, and will only be resolved and invoked explicitly | |
| where types are lazy by default, and will only bind when needed | |
| where holes are generally dormant but not lazy, and will be eager to run more implicitly than types or expressions | |
| where ( ) is a hole, and will automatically be filled by matching bindings in the current context | |
| where [ ] is a type, and will match and bind against other types | |
| where { } is an expression, and can be used to satisfy holes and types through it's return type | |
| where : is a binding, a pattern match between holes and types | |
| where ! resolves and invokes lazy expressions and types | |
| where , extends the expression on single lines similar to a newline | |
| where . refers to a context | |
| where \w+ is an identifier | |
| -] | |
| [- Bind a hole expecting itself by binding to itself as a hole, then bind a type that expects a single integer, and finally bind another hole that expects a boolean -] | |
| [- While this is an "incomplete" statement, it works because the holes and types are (mostly) lazy. -] | |
| [- This means we can "fill" the holes with more information later, as long as the holes bind correctly then statement will be considered complete -] | |
| [- The `. :` just means to bind to the current context, for types this is implicit if no other identifier is provided, for holes this matters more and should not be implicit -] | |
| yield | |
| : ( . : yield ) [ int ] ( . : bool ) | |
| [- Bind a hole expecting the bound yield statement, then expect a LinkedList by binding it to list in the type, and finally use those in context of the expression -] | |
| [- We then match the list, matching if it's empty or not, this part isn't as important and is more psuedocode than anything -] | |
| [- if the yield result is true then we continue iterating -] | |
| [- 'if' expects a boolean, and because we've satisfied two of the bound parts for yield (yield itself, through the hole bound at the beginning of iterate, and the type being satisfied by val we just need the boolean satisfied and it'll be ready to use -] | |
| [- Finally, we have a hole yield, satisfied by the first hole yield. It mostly acts as a convenience wrapper around this particular yield that's already partially bound -] | |
| iterate : ( yield ) [ . : list: LinkedList int ] { | |
| match list { | |
| | ( LinkedList.empty ) { } | |
| | ( [ val, next ] : LinkedList.item ) { | |
| if { yield val }, { iterate next } | |
| } | |
| } | |
| } | |
| [- Finally we arrive at the entrypoint for our program -] | |
| [- There are a few default bindings that can be matched against when starting the program -] | |
| [- Here, console is one of them -] | |
| [- We bind a new linked list to numbers -] | |
| [- Then we finally resolve and invoke the iterate binding, but there's a problem, the bool hole is still not satisfied, nothing has filled it -] | |
| main : ( console ) { | |
| numbers : LinkedList.new [1, 2, 3, 4, 5] | |
| iterate numbers ! | |
| } | |
| [- Let's fix that -] | |
| [- This time we'll create a binding that will satisfy the boolean hole, any expression will do so we'll do that -] | |
| [- This binding still needs to match the yield binding to bind to the iterate binding so we use those, this also lets us use the type `[ int ]`'s value inside of the expression, and because it's already bound we don't have to worry -] | |
| [- Finally we satisfy the iterate hole with it and, resolving all of the bindings and letting us compile and run the program, printing something like "[debug] item. 1, item. 7, item. 3" -] | |
| main : ( console ) { | |
| numbers : LinkedList.new [1, 7, 3, 2, 5, 9] | |
| iterator : ( yield ) [ n ] { | |
| console.debug "item. ", n | |
| n == 2 | |
| } | |
| iterate iterator { numbers } ! | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment