Finalize slides given timing constraints
Signed-off-by: Danila Fedorin <daniel.fedorin@hpe.com>
This commit is contained in:
BIN
type-level/linear-multistep.png
Normal file
BIN
type-level/linear-multistep.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.0 KiB |
@@ -235,6 +235,354 @@ There is no runtime overhead!
|
||||
<!-- _class: lead -->
|
||||
# Linear Multi-Step Method Approximator
|
||||
|
||||

|
||||
|
||||
---
|
||||
<!-- _class: lead -->
|
||||
|
||||
# Type-Safe `printf`
|
||||
|
||||
---
|
||||
|
||||
# The `printf` Function
|
||||
|
||||
The `printf` function accepts a format string, followed by a variable number of arguments that should match:
|
||||
|
||||
```C
|
||||
// totally fine:
|
||||
printf("Hello, %s! Your ChapelCon submission is #%d\n", "Daniel", 18);
|
||||
|
||||
// not good:
|
||||
printf("Hello, %s! Your ChapelCon submission is #%d\n", 18, "Daniel");
|
||||
```
|
||||
|
||||
Can we define a `printf` function in Chapel that is type-safe?
|
||||
|
||||
---
|
||||
|
||||
# Yet Another Type-Level List
|
||||
|
||||
- The general idea for type-safe `printf`: take the format string, and extract a list of the expected argument types.
|
||||
|
||||
- To make for nicer error messages, include a human-readable description of each type in the list.
|
||||
|
||||
- I've found it more convenient to re-define lists for various problems when needed, rather than having a single canonical list definition.
|
||||
|
||||
```chapel
|
||||
record _nil {
|
||||
proc type length param do return 0;
|
||||
}
|
||||
record _cons {
|
||||
type expectedType; // type of the argument to printf
|
||||
param name: string; // human-readable name of the type
|
||||
type rest;
|
||||
|
||||
proc type length param do return 1 + rest.length();
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Extracting Types from Format Strings
|
||||
|
||||
```Chapel
|
||||
proc specifiers(param s: string, param i: int = 0) type {
|
||||
if i >= s.size then return _nil;
|
||||
|
||||
if s[i] == "%" {
|
||||
if i + 1 >= s.size then
|
||||
compilerError("Invalid format string: unterminted %");
|
||||
|
||||
select s[i + 1] {
|
||||
when "%" do return specifiers(s, i + 2);
|
||||
when "s" do return _cons(string, "a string", specifiers(s, i + 2));
|
||||
when "i" do return _cons(int, "a signed integer", specifiers(s, i + 2));
|
||||
when "u" do return _cons(uint, "an unsigned integer", specifiers(s, i + 2));
|
||||
when "n" do return _cons(numeric, "a numeric value", specifiers(s, i + 2));
|
||||
otherwise do compilerError("Invalid format string: unknown format type");
|
||||
}
|
||||
} else {
|
||||
return specifiers(s, i + 1);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Extracting Types from Format Strings
|
||||
|
||||
Let's give it a quick try:
|
||||
|
||||
```Chapel
|
||||
writeln(specifiers("Hello, %s! Your ChapelCon submission is #%i\n") : string);
|
||||
```
|
||||
|
||||
The above prints:
|
||||
|
||||
```Chapel
|
||||
_cons(string,"a string",_cons(int(64),"a signed integer",_nil))
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Validating Argument Types
|
||||
|
||||
* The Chapel standard library has a nice `isSubtype` function that we can use to check if an argument matches the expected type.
|
||||
|
||||
* Suppose the `.length` of our type specifiers matches the number of arguments to `printf`
|
||||
|
||||
* Chapel doesn't currently support empty tuples, so if the lengths match, we know that `specifiers` is non-empty.
|
||||
|
||||
* Then, we can validate the types as follows:
|
||||
```Chapel
|
||||
proc validate(type specifiers: _cons(?t, ?s, ?rest), type argTup, param idx) {
|
||||
if !isSubtype(argTup[idx], t) then
|
||||
compilerError("Argument " + (idx + 1) : string + " should be " + s + " but got " + argTup[idx]:string, idx+2);
|
||||
|
||||
if idx + 1 < argTup.size then
|
||||
validate(rest, argTup, idx + 1);
|
||||
}
|
||||
```
|
||||
|
||||
* The `idx+2` argument to `compilerError` avoids printing the recursive `validate` calls in the error message.
|
||||
|
||||
---
|
||||
|
||||
# The `fprintln` overloads
|
||||
|
||||
* I named it `fprintln` for "formatted print line".
|
||||
|
||||
* To support the empty-specifier case (Chapel varargs don't allow zero arguments):
|
||||
|
||||
```Chapel
|
||||
proc fprintln(param format: string) where specifiers(format).length == 0 {
|
||||
writeln(format);
|
||||
}
|
||||
```
|
||||
* If we do have type specifiers, to ensure our earlier assumption of `size` matching:
|
||||
```Chapel
|
||||
proc fprintln(param format: string, args...)
|
||||
where specifiers(format).length != args.size {
|
||||
compilerError("'fprintln' with this format string expects " +
|
||||
specifiers(format).length : string +
|
||||
" argument(s) but got " + args.size : string);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# The `fprintln` overloads
|
||||
|
||||
* All that's left is the main `fprintln` implementation:
|
||||
|
||||
```Chapel
|
||||
proc fprintln(param format: string, args...) {
|
||||
validate(specifiers(format), args.type, 0);
|
||||
|
||||
writef(format + "\n", (...args));
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Using `fprintln`
|
||||
|
||||
```Chapel
|
||||
fprintln("Hello, world!"); // fine, prints "Hello, world!"
|
||||
fprintln("The answer is %i", 42); // fine, prints "The answer is 42"
|
||||
|
||||
// compiler error: Argument 3 should be a string but got int(64)
|
||||
fprintln("The answer is %i %i %s", 1, 2, 3);
|
||||
```
|
||||
|
||||
More work could be done to support more format specifiers, escapes, etc., but the basic idea is there.
|
||||
|
||||
---
|
||||
|
||||
<!-- _class: lead -->
|
||||
|
||||
# Beyond Lists
|
||||
|
||||
---
|
||||
|
||||
# Beyond Lists
|
||||
|
||||
* I made grand claims earlier
|
||||
- "Write functional-ish program at the type level!"
|
||||
* So far, we've just used lists and some recursion.
|
||||
* Is that all there is?
|
||||
|
||||
---
|
||||
|
||||
# Algebraic Data Types
|
||||
|
||||
* The kinds of data types that Haskell supports are called *algebraic data types*.
|
||||
* At a fundamental level, they can be built up from two operations: _Cartesian product_ and _disjoint union_.
|
||||
* There are other concepts to build recursive data types, but we won't need them in Chapel.
|
||||
- To prove to you I know what I'm talking about, some jargon:
|
||||
_initial algebras_, _the fixedpoint functor_, _catamorphisms_...
|
||||
- Check out _Bananas, Lenses, Envelopes and Barbed Wire_ by Meijer et al. for more.
|
||||
* __Claim__: Chapel supports disjoint union and Cartesian product, so we can build any data type that Haskell can.
|
||||
|
||||
---
|
||||
|
||||
<style scoped>
|
||||
li:nth-child(3) { color: lightgrey; }
|
||||
</style>
|
||||
|
||||
# Algebraic Data Types
|
||||
|
||||
- The kinds of data types that Haskell supports are called *algebraic data types*.
|
||||
- At a fundamental level, they can be built up from two operations: _Cartesian product_ and _disjoint union_.
|
||||
- There are other concepts to build recursive data types, but we won't need them in Chapel.
|
||||
- To prove to you I know what I'm talking about, some jargon:
|
||||
_initial algebras_, _the fixedpoint functor_, _catamorphisms_...
|
||||
- Check out _Bananas, Lenses, Envelopes and Barbed Wire_ by Meijer et al. for more.
|
||||
- __Claim__: Haskell supports disjoint union and Cartesian product, so we can build any data type that Haskell can.
|
||||
|
||||
---
|
||||
|
||||
# A General Recipe
|
||||
|
||||
To translate a Haskell data type definition to Chapel:
|
||||
|
||||
* For each constructor, define a `record` with that constructor's name
|
||||
* The fields of that record are `type` fields for each argument of the constructor
|
||||
- If the argument is a value (like `Int`), you can make it a `param` field instead
|
||||
* A visual example, again:
|
||||
|
||||
<div class="side-by-side">
|
||||
<div>
|
||||
|
||||
```Chapel
|
||||
record C1 { type arg1; /* ... */ type argi; }
|
||||
// ...
|
||||
record Cn { type arg1; /* ... */ type argj; }
|
||||
```
|
||||
</div>
|
||||
<div>
|
||||
|
||||
```Haskell
|
||||
data T = C1 arg1 ... argi
|
||||
| ...
|
||||
| Cn arg1 ... argj
|
||||
```
|
||||
</div>
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
# Inserting and Looking Up in a BST
|
||||
|
||||
<div class="side-by-side">
|
||||
<div>
|
||||
|
||||
```Chapel
|
||||
|
||||
proc insert(type t: Empty, param x: int) type do return Node(x, Empty, Empty);
|
||||
proc insert(type t: Node(?v, ?left, ?right), param x: int) type do
|
||||
select true {
|
||||
when x < v do return Node(v, insert(left, x), right);
|
||||
otherwise do return Node(v, left, insert(right, x));
|
||||
}
|
||||
|
||||
type test = insert(insert(insert(Empty, 2), 1), 3);
|
||||
|
||||
|
||||
|
||||
proc lookup(type t: Empty, param x: int) param do return false;
|
||||
proc lookup(type t: Node(?v, ?left, ?right), param x: int) param do
|
||||
select true {
|
||||
when x == v do return true;
|
||||
when x < v do return lookup(left, x);
|
||||
otherwise do return lookup(right, x);
|
||||
}
|
||||
```
|
||||
</div>
|
||||
<div>
|
||||
|
||||
```Haskell
|
||||
insert :: Int -> BSTree -> BSTree
|
||||
insert x Empty = Node x Empty Empty
|
||||
insert x (Node v left right)
|
||||
|
||||
| x < v = Node v (insert x left) right
|
||||
| otherwise = Node v left (insert x right)
|
||||
|
||||
|
||||
test = insert 3 (insert 1 (insert 2 Empty))
|
||||
|
||||
|
||||
lookup :: Int -> BSTree -> Bool
|
||||
lookup x Empty = False
|
||||
lookup x (Node v left right)
|
||||
|
||||
| x == v = True
|
||||
| x < v = lookup x left
|
||||
| otherwise = lookup x right
|
||||
|
||||
```
|
||||
</div>
|
||||
</div>
|
||||
|
||||
It really works!
|
||||
|
||||
```Chapel
|
||||
writeln(test : string);
|
||||
// prints Node(2,Node(1,Empty,Empty),Node(3,Empty,Empty))
|
||||
|
||||
writeln(lookup(test, 1));
|
||||
// prints true for this one, but false for '4'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# A Key-Value Map
|
||||
```Chapel
|
||||
record Empty {}
|
||||
record Node { param key: int; param value; type left; type right; }
|
||||
|
||||
proc insert(type t: Empty, param k: int, param v) type do return Node(k, v, Empty, Empty);
|
||||
proc insert(type t: Node(?k, ?v, ?left, ?right), param nk: int, param nv) type do
|
||||
select true {
|
||||
when nk < k do return Node(k, v, insert(left, nk, nv), right);
|
||||
otherwise do return Node(k, v, left, insert(right, nk, nv));
|
||||
}
|
||||
|
||||
proc lookup(type t: Empty, param k: int) param do return "not found";
|
||||
proc lookup(type t: Node(?k, ?v, ?left, ?right), param x: int) param do
|
||||
select true {
|
||||
when x == k do return v;
|
||||
when x < k do return lookup(left, x);
|
||||
otherwise do return lookup(right, x);
|
||||
}
|
||||
|
||||
type test = insert(insert(insert(Empty, 2, "two"), 1, "one"), 3, "three");
|
||||
writeln(lookup(test, 1)); // prints "one"
|
||||
writeln(lookup(test, 3)); // prints "three"
|
||||
writeln(lookup(test, 4)); // prints "not found"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Conclusion
|
||||
|
||||
* Chapel's type-level programming is surprisingly powerful.
|
||||
* We can write compile-time programs that are very similar to Haskell programs.
|
||||
* This allows us to write highly parameterized code without paying runtime overhead.
|
||||
* This also allows us to devise powerful compile-time checks and constraints on our code.
|
||||
* This approach allows for general-purpose programming, which can be applied to `your use-case`
|
||||
|
||||
---
|
||||
|
||||
<!-- _class: lead -->
|
||||
# Extra Slides
|
||||
|
||||
---
|
||||
|
||||
<!-- _class: lead -->
|
||||
# Linear Multi-Step Method Approximator
|
||||
|
||||
---
|
||||
|
||||
# Euler's Method
|
||||
@@ -433,210 +781,6 @@ We can now construct solvers for any explicit Adams-Bashforth method, without wr
|
||||
|
||||
---
|
||||
|
||||
<!-- _class: lead -->
|
||||
|
||||
# Type-Safe `printf`
|
||||
|
||||
---
|
||||
|
||||
# The `printf` Function
|
||||
|
||||
The `printf` function accepts a format string, followed by a variable number of arguments that should match:
|
||||
|
||||
```C
|
||||
// totally fine:
|
||||
printf("Hello, %s! Your ChapelCon submission is #%d\n", "Daniel", 18);
|
||||
|
||||
// not good:
|
||||
printf("Hello, %s! Your ChapelCon submission is #%d\n", 18, "Daniel");
|
||||
```
|
||||
|
||||
Can we define a `printf` function in Chapel that is type-safe?
|
||||
|
||||
---
|
||||
|
||||
# Yet Another Type-Level List
|
||||
|
||||
- The general idea for type-safe `printf`: take the format string, and extract a list of the expected argument types.
|
||||
|
||||
- To make for nicer error messages, include a human-readable description of each type in the list.
|
||||
|
||||
- I've found it more convenient to re-define lists for various problems when needed, rather than having a single canonical list definition.
|
||||
|
||||
```chapel
|
||||
record _nil {
|
||||
proc type length param do return 0;
|
||||
}
|
||||
record _cons {
|
||||
type expectedType; // type of the argument to printf
|
||||
param name: string; // human-readable name of the type
|
||||
type rest;
|
||||
|
||||
proc type length param do return 1 + rest.length();
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Extracting Types from Format Strings
|
||||
|
||||
```Chapel
|
||||
proc specifiers(param s: string, param i: int = 0) type {
|
||||
if i >= s.size then return _nil;
|
||||
|
||||
if s[i] == "%" {
|
||||
if i + 1 >= s.size then
|
||||
compilerError("Invalid format string: unterminted %");
|
||||
|
||||
select s[i + 1] {
|
||||
when "%" do return specifiers(s, i + 2);
|
||||
when "s" do return _cons(string, "a string", specifiers(s, i + 2));
|
||||
when "i" do return _cons(int, "a signed integer", specifiers(s, i + 2));
|
||||
when "u" do return _cons(uint, "an unsigned integer", specifiers(s, i + 2));
|
||||
when "n" do return _cons(numeric, "a numeric value", specifiers(s, i + 2));
|
||||
otherwise do compilerError("Invalid format string: unknown format type");
|
||||
}
|
||||
} else {
|
||||
return specifiers(s, i + 1);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Extracting Types from Format Strings
|
||||
|
||||
Let's give it a quick try:
|
||||
|
||||
```Chapel
|
||||
writeln(specifiers("Hello, %s! Your ChapelCon submission is #%i\n") : string);
|
||||
```
|
||||
|
||||
The above prints:
|
||||
|
||||
```Chapel
|
||||
_cons(string,"a string",_cons(int(64),"a signed integer",_nil))
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Validating Argument Types
|
||||
|
||||
* The Chapel standard library has a nice `isSubtype` function that we can use to check if an argument matches the expected type.
|
||||
|
||||
* Suppose the `.length` of our type specifiers matches the number of arguments to `printf`
|
||||
|
||||
* Chapel doesn't currently support empty tuples, so if the lengths match, we know that `specifiers` is non-empty.
|
||||
|
||||
* Then, we can validate the types as follows:
|
||||
```Chapel
|
||||
proc validate(type specifiers: _cons(?t, ?s, ?rest), type argTup, param idx) {
|
||||
if !isSubtype(argTup[idx], t) then
|
||||
compilerError("Argument " + (idx + 1) : string + " should be " + s + " but got " + argTup[idx]:string, idx+2);
|
||||
|
||||
if idx + 1 < argTup.size then
|
||||
validate(rest, argTup, idx + 1);
|
||||
}
|
||||
```
|
||||
|
||||
* The `idx+2` argument to `compilerError` avoids printing the recursive `validate` calls in the error message.
|
||||
|
||||
---
|
||||
|
||||
# The `fprintln` overloads
|
||||
|
||||
* I named it `fprintln` for "formatted print line".
|
||||
|
||||
* To support the empty-specifier case (Chapel varargs don't allow zero arguments):
|
||||
|
||||
```Chapel
|
||||
proc fprintln(param format: string) where specifiers(format).length == 0 {
|
||||
writeln(format);
|
||||
}
|
||||
```
|
||||
* If we do have type specifiers, to ensure our earlier assumption of `size` matching:
|
||||
```Chapel
|
||||
proc fprintln(param format: string, args...)
|
||||
where specifiers(format).length != args.size {
|
||||
compilerError("'fprintln' with this format string expects " +
|
||||
specifiers(format).length : string +
|
||||
" argument(s) but got " + args.size : string);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# The `fprintln` overloads
|
||||
|
||||
* All that's left is the main `fprintln` implementation:
|
||||
|
||||
```Chapel
|
||||
proc fprintln(param format: string, args...) {
|
||||
validate(specifiers(format), args.type, 0);
|
||||
|
||||
writef(format + "\n", (...args));
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Using `fprintln`
|
||||
|
||||
```Chapel
|
||||
fprintln("Hello, world!"); // fine, prints "Hello, world!"
|
||||
fprintln("The answer is %i", 42); // fine, prints "The answer is 42"
|
||||
|
||||
// compiler error: Argument 3 should be a string but got int(64)
|
||||
fprintln("The answer is %i %i %s", 1, 2, 3);
|
||||
```
|
||||
|
||||
More work could be done to support more format specifiers, escapes, etc., but the basic idea is there.
|
||||
|
||||
---
|
||||
|
||||
<!-- _class: lead -->
|
||||
|
||||
# Beyond Lists
|
||||
|
||||
---
|
||||
|
||||
# Beyond Lists
|
||||
|
||||
* I made grand claims earlier
|
||||
- "Write functional-ish program at the type level!"
|
||||
* So far, we've just used lists and some recursion.
|
||||
* Is that all there is?
|
||||
|
||||
---
|
||||
|
||||
# Algebraic Data Types
|
||||
|
||||
* The kinds of data types that Haskell supports are called *algebraic data types*.
|
||||
* At a fundamental level, they can be built up from two operations: _Cartesian product_ and _disjoint union_.
|
||||
* There are other concepts to build recursive data types, but we won't need them in Chapel.
|
||||
- To prove to you I know what I'm talking about, some jargon:
|
||||
_initial algebras_, _the fixedpoint functor_, _catamorphisms_...
|
||||
- Check out _Bananas, Lenses, Envelopes and Barbed Wire_ by Meijer et al. for more.
|
||||
* __This matters because, if Chapel has these operations, we can build any data type that Haskell can.__
|
||||
|
||||
---
|
||||
|
||||
<style scoped>
|
||||
li:nth-child(3) { color: lightgrey; }
|
||||
</style>
|
||||
|
||||
# Algebraic Data Types
|
||||
|
||||
- The kinds of data types that Haskell supports are called *algebraic data types*.
|
||||
- At a fundamental level, they can be built up from two operations: _Cartesian product_ and _disjoint union_.
|
||||
- There are other concepts to build recursive data types, but we won't need them in Chapel.
|
||||
- To prove to you I know what I'm talking about, some jargon:
|
||||
_initial algebras_, _the fixedpoint functor_, _catamorphisms_...
|
||||
- Check out _Bananas, Lenses, Envelopes and Barbed Wire_ by Meijer et al. for more.
|
||||
- __This matters because, if Chapel has these operations, we can build any data type that Haskell can.__
|
||||
|
||||
---
|
||||
|
||||
# Cartesian Product
|
||||
For any two types, the _Cartesian product_ of these two types defines all pairs of values from these types.
|
||||
- This is like a two-element tuple _at the value level_ in Chapel.
|
||||
@@ -853,133 +997,3 @@ balancedOneTwoThree' = InR (2 `MkPair` (InR (1 `MkPair` (InL MkUnit `MkPair` InL
|
||||
|
||||
---
|
||||
|
||||
# A General Recipe
|
||||
|
||||
To translate a Haskell data type definition to Chapel:
|
||||
|
||||
* For each constructor, define a `record` with that constructor's name
|
||||
* The fields of that record are `type` fields for each argument of the constructor
|
||||
- If the argument is a value (like `Int`), you can make it a `param` field instead
|
||||
* A visual example, again:
|
||||
|
||||
<div class="side-by-side">
|
||||
<div>
|
||||
|
||||
```Chapel
|
||||
record C1 { type arg1; /* ... */ type argi; }
|
||||
// ...
|
||||
record Cn { type arg1; /* ... */ type argj; }
|
||||
```
|
||||
</div>
|
||||
<div>
|
||||
|
||||
```Haskell
|
||||
data T = C1 arg1 ... argi
|
||||
| ...
|
||||
| Cn arg1 ... argj
|
||||
```
|
||||
</div>
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
# Inserting and Looking Up in a BST
|
||||
|
||||
<div class="side-by-side">
|
||||
<div>
|
||||
|
||||
```Chapel
|
||||
|
||||
proc insert(type t: Empty, param x: int) type do return Node(x, Empty, Empty);
|
||||
proc insert(type t: Node(?v, ?left, ?right), param x: int) type do
|
||||
select true {
|
||||
when x < v do return Node(v, insert(left, x), right);
|
||||
otherwise do return Node(v, left, insert(right, x));
|
||||
}
|
||||
|
||||
type test = insert(insert(insert(Empty, 2), 1), 3);
|
||||
|
||||
|
||||
|
||||
proc lookup(type t: Empty, param x: int) param do return false;
|
||||
proc lookup(type t: Node(?v, ?left, ?right), param x: int) param do
|
||||
select true {
|
||||
when x == v do return true;
|
||||
when x < v do return lookup(left, x);
|
||||
otherwise do return lookup(right, x);
|
||||
}
|
||||
```
|
||||
</div>
|
||||
<div>
|
||||
|
||||
```Haskell
|
||||
insert :: Int -> BSTree -> BSTree
|
||||
insert x Empty = Node x Empty Empty
|
||||
insert x (Node v left right)
|
||||
|
||||
| x < v = Node v (insert x left) right
|
||||
| otherwise = Node v left (insert x right)
|
||||
|
||||
|
||||
test = insert 3 (insert 1 (insert 2 Empty))
|
||||
|
||||
|
||||
lookup :: Int -> BSTree -> Bool
|
||||
lookup x Empty = False
|
||||
lookup x (Node v left right)
|
||||
|
||||
| x == v = True
|
||||
| x < v = lookup x left
|
||||
| otherwise = lookup x right
|
||||
|
||||
```
|
||||
</div>
|
||||
</div>
|
||||
|
||||
It really works!
|
||||
|
||||
```Chapel
|
||||
writeln(test : string);
|
||||
// prints Node(2,Node(1,Empty,Empty),Node(3,Empty,Empty))
|
||||
|
||||
writeln(lookup(test, 1));
|
||||
// prints true for this one, but false for '4'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# A Key-Value Map
|
||||
```Chapel
|
||||
record Empty {}
|
||||
record Node { param key: int; param value; type left; type right; }
|
||||
|
||||
proc insert(type t: Empty, param k: int, param v) type do return Node(k, v, Empty, Empty);
|
||||
proc insert(type t: Node(?k, ?v, ?left, ?right), param nk: int, param nv) type do
|
||||
select true {
|
||||
when nk < k do return Node(k, v, insert(left, nk, nv), right);
|
||||
otherwise do return Node(k, v, left, insert(right, nk, nv));
|
||||
}
|
||||
|
||||
proc lookup(type t: Empty, param k: int) param do return "not found";
|
||||
proc lookup(type t: Node(?k, ?v, ?left, ?right), param x: int) param do
|
||||
select true {
|
||||
when x == k do return v;
|
||||
when x < k do return lookup(left, x);
|
||||
otherwise do return lookup(right, x);
|
||||
}
|
||||
|
||||
type test = insert(insert(insert(Empty, 2, "two"), 1, "one"), 3, "three");
|
||||
writeln(lookup(test, 1)); // prints "one"
|
||||
writeln(lookup(test, 3)); // prints "three"
|
||||
writeln(lookup(test, 4)); // prints "not found"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Conclusion
|
||||
|
||||
* Chapel's type-level programming is surprisingly powerful.
|
||||
* We can write compile-time programs that are very similar to Haskell programs.
|
||||
* This allows us to write highly parameterized code without paying runtime overhead.
|
||||
* This also allows us to devise powerful compile-time checks and constraints on our code.
|
||||
* This approach allows for general-purpose programming, which can be applied to `your use-case`
|
||||
|
||||
Reference in New Issue
Block a user