Document not found (404)
+This URL is invalid, sorry. Please use the navigation bar or search to continue.
+ +diff --git a/book/.nojekyll b/book/.nojekyll new file mode 100644 index 000000000..f17311098 --- /dev/null +++ b/book/.nojekyll @@ -0,0 +1 @@ +This file makes sure that Github Pages doesn't process mdBook's output. diff --git a/book/404.html b/book/404.html new file mode 100644 index 000000000..94c270b5e --- /dev/null +++ b/book/404.html @@ -0,0 +1,218 @@ + + +
+ + +This URL is invalid, sorry. Please use the navigation bar or search to continue.
+ +Returns the absolute value of the given input
+Returns the first element of the list l.
+(car l) -> any/c
+> (car '(1 2)) ;; => 1
+> (car (cons 2 3)) ;; => 2
+
+Checks if two characters are equal
+Requires that the two inputs are both characters, and will otherwise +raise an error.
+Recursively copies the directory from source to destination
+Creates the directory
+Check the current working directory
+Deletes the directory
+Checks if the list is empty
+(empty? lst) -> bool?
+> (empty? (list 1 2 3 4 5)) ;; => #false
+> (empty? '()) ;; => #true
+
+Checks if the input string ends with a given suffix
+(ends-with? input pattern) -> bool?
+input : string? +pattern: string?
+> (ends-with? "foobar" "foo") ;; => #false
+> (ends-with? "foobar" "bar") ;; => #true
+
+Returns Euler's number raised to the power of z.
+Gets the filename for a given path
+Returns the first element of the list l.
+(first l) -> any/c
+> (first '(1 2)) ;; => 1
+> (first (cons 2 3)) ;; => 2
+
+Checks whether the given map contains the given key. Key must be hashable.
+(hash-contains? map key) -> bool?
+> (hash-contains? (hash 'a 10 'b 20) 'a) ;; => #true
+> (hash-contains? (hash 'a 10 'b 20) 'not-there) ;; => #false
+
+Returns a new hashmap with the additional key value pair added. Performs a functional update, +so the old hash map is still accessible.
+(hash-insert map key val) -> hash?
+> (hash-insert (hash 'a 10 'b 20) 'c 30)
+
+=> #<hashmap {
+'a: 10,
+'b: 20,
+'c: 30
+}>
+
+Returns the keys of the given hash map as a list.
+(hash-keys->list map) -> (listof hashable?)
+
+> (hash-keys->list? (hash 'a 'b 20)) ;; => '(a b)
+
+Returns the number of key value pairs in the map
+(hash-length map) -> (and positive? int?)
+> (hash-length (hash 'a 10 'b 20)) ;; => 2
+
+Gets the key
from the given map
. Raises an error if the key does not exist. hash-get
is an alias for this.
(hash-ref map key) -> any/c
+> (hash-ref (hash 'a 10 'b 20) 'b) ;; => 20
+
+Gets the key
from the given map
. Returns #false if the key does not exist.
(hash-try-get map key) -> (or any/c #false)
+> (hash-try-get (hash 'a 10 'b 20) 'b) ;; => 20
+> (hash-try-get (hash 'a 10 'b 20) 'does-not-exist) ;; => #false
+
+Checks if a given value is an input port
+(input-port? any/c) -> bool?
+> (input-port? (stdin)) ;; => #true
+> (input-port? "foo") ;; => #false
+
+Converts an integer into a string.
+(int->string int?) -> string?
+> (int->string 10) ;; => "10"
+
+Checks if a path is a directory
+Checks if a path is a file
+Returns the length of the list.
+(length l) -> int?
+> (length (list 10 20 30)) ;; => 3
+
+Returns a newly allocated list containing the vs as its elements.
+(list v ...) -> list?
+> (list 1 2 3 4 5) ;; => '(1 2 3 4 5)
+> (list (list 1 2) (list 3 4)) ;; => '((1 2) (3 4))
+
+Returns the value located at the given index. Will raise an error if you try to index out of bounds.
+Note: Runs in time proportional to the length of the list, however lists in Steel are implemented in such a fashion that the +time complexity is O(n/64). Meaning, for small lists this can be constant.
+(list-ref lst index) -> list?
+> (list-ref (list 1 2 3 4) 2) ;; => 3
+> (list-ref (range 0 100) 42) ;; => 42"
+> (list-ref (list 1 2 3 4) 10)
+error[E11]: Generic
+┌─ :1:2
+│
+1 │ (list-ref (list 1 2 3 4) 10)
+│ ^^^^^^^^ out of bounds index in list-ref - list length: 4, index: 10
+
+Converts the given number to a string
+Takes a filename path
referring to an existing file and returns an input port. Raises an error
+if the file does not exist
(open-input-file string?) -> input-port?
+> (open-input-file "foo-bar.txt") ;; => #<port>
+> (open-input-file "file-does-not-exist.txt")
+error[E08]: Io
+┌─ :1:2
+│
+1 │ (open-input-file "foo-bar.txt")
+│ ^^^^^^^^^^^^^^^ No such file or directory (os error 2)
+
+Takes a filename path
referring to a file to be created and returns an output port.
(open-output-file string?) -> output-port?
+> (open-output-file "foo-bar.txt") ;; => #<port>
+
+Checks if a given value is an output port
+(output-port? any/c) -> bool?
+> (define output (open-output-file "foo.txt"))
+> (output-port? output) ;; => #true
+
+Checks if the given value can be treated as a pair. +Note - there are no improper lists in steel, so any list with at least one element +is considered a pair.
+(pair? any/c) -> bool?
+> (pair? '(10 20)) ;; => #true
+> (pair? '(10)) ;; => #true
+> (pair? '()) ;; => #false
+
+Gets the extension from a path
+Checks if a path exists
+Returns a newly allocated list of the elements in the range (n, m]
+(range n m) -> (listof int?)
+> (range 0 10) ;; => '(0 1 2 3 4 5 6 7 8 9)
+
+Returns the contents of the directory as a list
+Takes a port and reads the entire content into a string
+(read-port-to-string port) -> string?
+Get the second element of the list. Raises an error if the list does not have an element in the second position.
+(second l) -> any/c
+> (second '(1 2 3)) ;; => 2
+> (second '())
+error[E11]: Generic
+┌─ :1:2
+│
+1 │ (second '())
+│ ^^^^^^ second: index out of bounds - list did not have an element in the second position: []
+### **split-whitespace**
+Returns a list of strings from the original string split on the whitespace
+
+(split-whitespace string?) -> (listof string?)
+
+#### Examples
+
+```scheme
+(split-whitespace "apples bananas fruits veggies") ;; '("apples" "bananas" "fruits" "veggies")
+
+Checks if the input string starts with a prefix
+(starts-with? input pattern) -> bool?
+input : string? +pattern: string?
+> (starts-with? "foobar" "foo") ;; => #true
+> (starts-with? "foobar" "bar") ;; => #false
+
+Gets the port handle to stdin
+(stdin) -> input-port?
+> (stdin) ;; => #<port>
+
+Constructs a string from the given characters
+Converts a string into an int. Raises an error if the string cannot be converted to an integer.
+(string->int string?) -> int?
+> (string->int "100") ;; => 10
+> (string->int "not-an-int") ;; error
+
+Converts a string into a list of characters.
+(string->list string?) -> (listof char?)
+> (string->list "hello") ;; => '(#\h #\e #\l #\l #\o)
+
+Creates a new lowercased version of the input string
+(string->lower string?) -> string?
+> (string->lower "sPonGeBoB tExT") ;; => "spongebob text"
+
+Converts the given string to a number
+Converts a string into a symbol.
+(string->symbol string?) -> symbol?
+> (string->symbol "FooBar") ;; => 'FooBar
+
+Creates a new uppercased version of the input string
+(string->upper string?) -> string?
+> (string->upper "lower") ;; => "LOWER"
+
+Concatenates all of the given strings into one
+(string-append strs...) -> string?
+> (string-append) ;; => ""
+> (string-append "foo" "bar") ;; => "foobar"
+### **string-length**
+Get the length of the given string
+
+(string-length string?) -> int?
+
+#### Examples
+
+```scheme
+> (string-length "apples") ;; => 6
+
+Returns the first n elements of the list l as a new list.
+(take l n) -> list?
+> (take '(1 2 3 4) 2) ;; => '(0 1)
+> (take (range 0 10) 4) ;; => '(0 1 2 3)
+
+Get the third element of the list. Raises an error if the list does not have an element in the third position.
+(third l) -> any/c
+> (third '(1 2 3)) ;; => 3
+> (third '())
+error[E11]: Generic
+┌─ :1:2
+│
+1 │ (third '())
+│ ^^^^^^ third: index out of bounds - list did not have an element in the second position: []
+
+Concatenatives all of the inputs to their string representation, separated by spaces.
+(to-string xs ...)
+> (to-string 10) ;; => "10"
+> (to-string "hello" "world") ;; => "hello world"
+
+Returns a new string with the leading and trailing whitespace removed.
+(trim string?) -> string?
+> (trim " foo ") ;; => "foo"
+
+Returns a new string with the trailing whitespace removed.
+(trim string?) -> string?
+> (trim " foo ") ;; => " foo"
+
+Returns a new string with the given pat
repeatedly removed from the end
+of the string
(trim-end-matches string? string?) -> string?
+
+> (trim-end-matches "123foo1bar123123" "123") ;; => "123foo1bar"
+
+Returns a new string with the leading whitespace removed.
+(trim string?) -> string?
+> (trim " foo ") ;; => "foo "
+
+Returns a new string with the given pat
repeatedly removed from the start
+of the string
(trim-start-matches string? string?) -> string?
+
+> (trim-start-matches "123foo1bar123123" "123") ;; => "foo1bar123123"
+
+Lists in Steel have an interface that matches those of classic schemes or lisps. +At face value, they appear to be implemented as cons cells - however, under the hood +they are actually implemented as unrolled linked lists.
+This means that for most practical purposes, interaction with lists is the same. +That being said, there are no improper lists, meaning, pairs are actually just lists of two elements.
+Indexing into a list also takes O(n/64) - which means you'll get constant time indexing on small lists.
+(list 10 20 30 40) ;; => '(10 20 30 40)
+
+Returns the first element of the list l.
+(car l) -> any/c
+> (car '(1 2)) ;; => 1
+> (car (cons 2 3)) ;; => 2
+
+Checks if the list is empty
+(empty? lst) -> bool?
+> (empty? (list 1 2 3 4 5)) ;; => #false
+> (empty? '()) ;; => #true
+
+Returns the first element of the list l.
+(first l) -> any/c
+> (first '(1 2)) ;; => 1
+> (first (cons 2 3)) ;; => 2
+
+Returns the length of the list.
+(length l) -> int?
+> (length (list 10 20 30)) ;; => 3
+
+Returns a newly allocated list containing the vs as its elements.
+(list v ...) -> list?
+> (list 1 2 3 4 5) ;; => '(1 2 3 4 5)
+> (list (list 1 2) (list 3 4)) ;; => '((1 2) (3 4))
+
+Returns the value located at the given index. Will raise an error if you try to index out of bounds.
+Note: Runs in time proportional to the length of the list, however lists in Steel are implemented in such a fashion that the +time complexity is O(n/64). Meaning, for small lists this can be constant.
+(list-ref lst index) -> list?
+> (list-ref (list 1 2 3 4) 2) ;; => 3
+> (list-ref (range 0 100) 42) ;; => 42"
+> (list-ref (list 1 2 3 4) 10)
+error[E11]: Generic
+┌─ :1:2
+│
+1 │ (list-ref (list 1 2 3 4) 10)
+│ ^^^^^^^^ out of bounds index in list-ref - list length: 4, index: 10
+
+Checks if the given value can be treated as a pair. +Note - there are no improper lists in steel, so any list with at least one element +is considered a pair.
+(pair? any/c) -> bool?
+> (pair? '(10 20)) ;; => #true
+> (pair? '(10)) ;; => #true
+> (pair? '()) ;; => #false
+
+Returns a newly allocated list of the elements in the range (n, m]
+(range n m) -> (listof int?)
+> (range 0 10) ;; => '(0 1 2 3 4 5 6 7 8 9)
+
+Get the second element of the list. Raises an error if the list does not have an element in the second position.
+(second l) -> any/c
+> (second '(1 2 3)) ;; => 2
+> (second '())
+error[E11]: Generic
+┌─ :1:2
+│
+1 │ (second '())
+│ ^^^^^^ second: index out of bounds - list did not have an element in the second position: []
+### **take**
+Returns the first n elements of the list l as a new list.
+
+(take l n) -> list?
+
+* l : list?
+* n : (and/c positive? int?)
+
+#### Examples
+
+```scheme
+> (take '(1 2 3 4) 2) ;; => '(0 1)
+> (take (range 0 10) 4) ;; => '(0 1 2 3)
+
+Get the third element of the list. Raises an error if the list does not have an element in the third position.
+(third l) -> any/c
+> (third '(1 2 3)) ;; => 3
+> (third '())
+error[E11]: Generic
+┌─ :1:2
+│
+1 │ (third '())
+│ ^^^^^^ third: index out of bounds - list did not have an element in the second position: []
+
+Strings in Steel are immutable, fixed length arrays of characters. They are heap allocated,
+and are implemented under the hood as referenced counted rust Strings
.
Checks if two characters are equal
+Requires that the two inputs are both characters, and will otherwise +raise an error.
+Checks if the input string ends with a given suffix
+(ends-with? input pattern) -> bool?
+input : string? +pattern: string?
+> (ends-with? "foobar" "foo") ;; => #false
+> (ends-with? "foobar" "bar") ;; => #true
+
+Converts an integer into a string.
+(int->string int?) -> string?
+> (int->string 10) ;; => "10"
+
+Converts the given number to a string
+Returns a list of strings from the original string split on the whitespace
+(split-whitespace string?) -> (listof string?)
+(split-whitespace "apples bananas fruits veggies") ;; '("apples" "bananas" "fruits" "veggies")
+
+Checks if the input string starts with a prefix
+(starts-with? input pattern) -> bool?
+input : string? +pattern: string?
+> (starts-with? "foobar" "foo") ;; => #true
+> (starts-with? "foobar" "bar") ;; => #false
+
+Constructs a string from the given characters
+Converts a string into an int. Raises an error if the string cannot be converted to an integer.
+(string->int string?) -> int?
+> (string->int "100") ;; => 10
+> (string->int "not-an-int") ;; error
+
+Converts a string into a list of characters.
+(string->list string?) -> (listof char?)
+> (string->list "hello") ;; => '(#\h #\e #\l #\l #\o)
+
+Creates a new lowercased version of the input string
+(string->lower string?) -> string?
+> (string->lower "sPonGeBoB tExT") ;; => "spongebob text"
+
+Converts the given string to a number
+Converts a string into a symbol.
+(string->symbol string?) -> symbol?
+> (string->symbol "FooBar") ;; => 'FooBar
+
+Creates a new uppercased version of the input string
+(string->upper string?) -> string?
+> (string->upper "lower") ;; => "LOWER"
+
+Concatenates all of the given strings into one
+(string-append strs...) -> string?
+> (string-append) ;; => ""
+> (string-append "foo" "bar") ;; => "foobar"
+### **string-length**
+Get the length of the given string
+
+(string-length string?) -> int?
+
+#### Examples
+
+```scheme
+> (string-length "apples") ;; => 6
+
+Concatenatives all of the inputs to their string representation, separated by spaces.
+(to-string xs ...)
+> (to-string 10) ;; => "10"
+> (to-string "hello" "world") ;; => "hello world"
+
+Returns a new string with the leading and trailing whitespace removed.
+(trim string?) -> string?
+> (trim " foo ") ;; => "foo"
+
+Returns a new string with the trailing whitespace removed.
+(trim string?) -> string?
+> (trim " foo ") ;; => " foo"
+
+Returns a new string with the given pat
repeatedly removed from the end
+of the string
(trim-end-matches string? string?) -> string?
+
+> (trim-end-matches "123foo1bar123123" "123") ;; => "123foo1bar"
+
+Returns a new string with the leading whitespace removed.
+(trim string?) -> string?
+> (trim " foo ") ;; => "foo "
+
+Returns a new string with the given pat
repeatedly removed from the start
+of the string
(trim-start-matches string? string?) -> string?
+
+> (trim-start-matches "123foo1bar123123" "123") ;; => "foo1bar123123"
+
+Contains direct wrappers around the Rust std::time::Instant
and std::time::Duration
modules.
+For example, to measure the time something takes:
(define t (instant/now))
+(displayln "Hello world")
+(displayln (instant/elapsed t))
+
+Rust values, types, and functions are easily embedded into Steel. Using the register_fn
call, you can embed functions easily:
use steel_vm::engine::Engine;
+use steel_vm::register_fn::RegisterFn;
+
+fn external_function(arg1: usize, arg2: usize) -> usize {
+ arg1 + arg2
+}
+
+fn option_function(arg1: Option<String>) -> Option<String> {
+ arg1
+}
+
+fn result_function(arg1: Option<String>) -> Result<String, String> {
+ if let Some(inner) = arg1 {
+ Ok(inner)
+ } else {
+ Err("Got a none".to_string())
+ }
+}
+
+pub fn main() {
+ let mut vm = Engine::new();
+
+ // Here we can register functions
+ // Any function can accept parameters that implement `FromSteelVal` and
+ // return values that implement `IntoSteelVal`
+ vm.register_fn("external-function", external_function);
+
+ // See the docs for more information about `FromSteelVal` and `IntoSteelVal`
+ // but we can see even functions that accept/return Option<T> or Result<T,E>
+ // can be registered
+ vm.register_fn("option-function", option_function);
+
+ // Result values will map directly to errors in the VM and bubble back up
+ vm.register_fn("result-function", result_function);
+
+ vm.run(
+ r#"
+ (define foo (external-function 10 25))
+ (define bar (option-function "applesauce"))
+ (define baz (result-function "bananas"))
+ "#,
+ )
+ .unwrap();
+
+ let foo = vm.extract::<usize>("foo").unwrap();
+ println!("foo: {}", foo);
+ assert_eq!(35, foo);
+
+ // Can also extract a value by specifying the type on the variable
+ let bar: String = vm.extract("bar").unwrap();
+ println!("bar: {}", bar);
+ assert_eq!("applesauce".to_string(), bar);
+
+ let baz: String = vm.extract("baz").unwrap();
+ println!("baz: {}", baz);
+ assert_eq!("bananas".to_string(), baz);
+}
+We can also embed structs themselves:
+use steel_vm::engine::Engine;
+use steel_vm::register_fn::RegisterFn;
+
+use steel_derive::Steel;
+
+// In order to register a type with Steel,
+// it must implement Clone, Debug, and Steel
+#[derive(Clone, Debug, Steel, PartialEq)]
+pub struct ExternalStruct {
+ foo: usize,
+ bar: String,
+ baz: f64,
+}
+
+impl ExternalStruct {
+ pub fn new(foo: usize, bar: String, baz: f64) -> Self {
+ ExternalStruct { foo, bar, baz }
+ }
+
+ // Embedding functions that take self must take by value
+ pub fn method_by_value(self) -> usize {
+ self.foo
+ }
+
+ // Setters should update the value and return a new instance (functional set)
+ pub fn set_foo(mut self, foo: usize) -> Self {
+ self.foo = foo;
+ self
+ }
+}
+
+pub fn main() {
+ let mut vm = Engine::new();
+
+ // Registering a type gives access to a predicate for the type
+ vm.register_type::<ExternalStruct>("ExternalStruct?");
+
+ // Structs in steel typically have a constructor that is the name of the struct
+ vm.register_fn("ExternalStruct", ExternalStruct::new);
+
+ // register_fn can be chained
+ vm.register_fn("method-by-value", ExternalStruct::method_by_value)
+ .register_fn("set-foo", ExternalStruct::set_foo);
+
+ let external_struct = ExternalStruct::new(1, "foo".to_string(), 12.4);
+
+ // Registering an external value is fallible if the conversion fails for some reason
+ // For instance, registering an Err(T) is fallible. However, most implementation outside of manual
+ // ones should not fail
+ vm.register_external_value("external-struct", external_struct)
+ .unwrap();
+
+ let output = vm
+ .run(
+ r#"
+ (define new-external-struct (set-foo external-struct 100))
+ (define get-output (method-by-value external-struct))
+ (define second-new-external-struct (ExternalStruct 50 "bananas" 72.6))
+ "last-result"
+ "#,
+ )
+ .unwrap();
+
+ let new_external_struct = vm.extract::<ExternalStruct>("new-external-struct").unwrap();
+ println!("new_external_struct: {:?}", new_external_struct);
+ assert_eq!(
+ ExternalStruct::new(100, "foo".to_string(), 12.4),
+ new_external_struct
+ );
+
+ // Can also extract a value by specifying the type on the variable
+ let get_output: usize = vm.extract("get-output").unwrap();
+ println!("get_output: {}", get_output);
+ assert_eq!(1, get_output);
+
+ let second_new_external_struct: ExternalStruct =
+ vm.extract("second-new-external-struct").unwrap();
+ println!(
+ "second_new_external_struct: {:?}",
+ second_new_external_struct
+ );
+ assert_eq!(
+ ExternalStruct::new(50, "bananas".to_string(), 72.6),
+ second_new_external_struct
+ );
+
+ // We also get the output of the VM as the value of every expression run
+ // we can inspect the results just by printing like so
+ println!("{:?}", output);
+}
+Types that implement IntoSteelVal
and FromSteelVal
and be returned and passed into rust functions, respectively. Take the following for example:
+#![allow(unused)] + +fn main() { +fn foo(value: isize) -> String { + ... +} + +}
This means that steel values will attempt to be coerced to the type in the function signature, and the value that this function returns will then attempt to be coerced into a that Steel understands.
+There are some special case conversions that happen specifically:
+IntoSteelVal
Ok(T)
then T else (error E)
Some(T)
then T else #false
(log! level arg-list)
+
+Log directly on the specified level the with arguments, as a list
+(log/info! . args)
+
+Log the arguments using the info target, i.e. log on INFO
+(log/warn! . args)
+
+Log the arguments using the warn target, i.e. log on WARN
+(log/debug! . args)
+
+Log the arguments using the debug target, i.e. log on DEBUG
+(log/error! . args)
+
+Log the arguments using the error target, i.e. log on ERROR
+(log/trace! . args)
+
+Log the arguments using the trace target, i.e. log on TRACE
+ +Rust values, types, and functions are easily embedded into Steel. Using the register_fn
call, you can embed functions easily:
use steel_vm::engine::Engine;
+use steel_vm::register_fn::RegisterFn;
+
+fn external_function(arg1: usize, arg2: usize) -> usize {
+ arg1 + arg2
+}
+
+fn option_function(arg1: Option<String>) -> Option<String> {
+ arg1
+}
+
+fn result_function(arg1: Option<String>) -> Result<String, String> {
+ if let Some(inner) = arg1 {
+ Ok(inner)
+ } else {
+ Err("Got a none".to_string())
+ }
+}
+
+pub fn main() {
+ let mut vm = Engine::new();
+
+ // Here we can register functions
+ // Any function can accept parameters that implement `FromSteelVal` and
+ // return values that implement `IntoSteelVal`
+ vm.register_fn("external-function", external_function);
+
+ // See the docs for more information about `FromSteelVal` and `IntoSteelVal`
+ // but we can see even functions that accept/return Option<T> or Result<T,E>
+ // can be registered
+ vm.register_fn("option-function", option_function);
+
+ // Result values will map directly to errors in the VM and bubble back up
+ vm.register_fn("result-function", result_function);
+
+ vm.run(
+ r#"
+ (define foo (external-function 10 25))
+ (define bar (option-function "applesauce"))
+ (define baz (result-function "bananas"))
+ "#,
+ )
+ .unwrap();
+
+ let foo = vm.extract::<usize>("foo").unwrap();
+ println!("foo: {}", foo);
+ assert_eq!(35, foo);
+
+ // Can also extract a value by specifying the type on the variable
+ let bar: String = vm.extract("bar").unwrap();
+ println!("bar: {}", bar);
+ assert_eq!("applesauce".to_string(), bar);
+
+ let baz: String = vm.extract("baz").unwrap();
+ println!("baz: {}", baz);
+ assert_eq!("bananas".to_string(), baz);
+}
+We can also embed structs themselves:
+use steel_vm::engine::Engine;
+use steel_vm::register_fn::RegisterFn;
+
+use steel_derive::Steel;
+
+// In order to register a type with Steel,
+// it must implement Clone, Debug, and Steel
+#[derive(Clone, Debug, Steel, PartialEq)]
+pub struct ExternalStruct {
+ foo: usize,
+ bar: String,
+ baz: f64,
+}
+
+impl ExternalStruct {
+ pub fn new(foo: usize, bar: String, baz: f64) -> Self {
+ ExternalStruct { foo, bar, baz }
+ }
+
+ // Embedding functions that take self must take by value
+ pub fn method_by_value(self) -> usize {
+ self.foo
+ }
+
+ // Setters should update the value and return a new instance (functional set)
+ pub fn set_foo(mut self, foo: usize) -> Self {
+ self.foo = foo;
+ self
+ }
+}
+
+pub fn main() {
+ let mut vm = Engine::new();
+
+ // Registering a type gives access to a predicate for the type
+ vm.register_type::<ExternalStruct>("ExternalStruct?");
+
+ // Structs in steel typically have a constructor that is the name of the struct
+ vm.register_fn("ExternalStruct", ExternalStruct::new);
+
+ // register_fn can be chained
+ vm.register_fn("method-by-value", ExternalStruct::method_by_value)
+ .register_fn("set-foo", ExternalStruct::set_foo);
+
+ let external_struct = ExternalStruct::new(1, "foo".to_string(), 12.4);
+
+ // Registering an external value is fallible if the conversion fails for some reason
+ // For instance, registering an Err(T) is fallible. However, most implementation outside of manual
+ // ones should not fail
+ vm.register_external_value("external-struct", external_struct)
+ .unwrap();
+
+ let output = vm
+ .run(
+ r#"
+ (define new-external-struct (set-foo external-struct 100))
+ (define get-output (method-by-value external-struct))
+ (define second-new-external-struct (ExternalStruct 50 "bananas" 72.6))
+ "last-result"
+ "#,
+ )
+ .unwrap();
+
+ let new_external_struct = vm.extract::<ExternalStruct>("new-external-struct").unwrap();
+ println!("new_external_struct: {:?}", new_external_struct);
+ assert_eq!(
+ ExternalStruct::new(100, "foo".to_string(), 12.4),
+ new_external_struct
+ );
+
+ // Can also extract a value by specifying the type on the variable
+ let get_output: usize = vm.extract("get-output").unwrap();
+ println!("get_output: {}", get_output);
+ assert_eq!(1, get_output);
+
+ let second_new_external_struct: ExternalStruct =
+ vm.extract("second-new-external-struct").unwrap();
+ println!(
+ "second_new_external_struct: {:?}",
+ second_new_external_struct
+ );
+ assert_eq!(
+ ExternalStruct::new(50, "bananas".to_string(), 72.6),
+ second_new_external_struct
+ );
+
+ // We also get the output of the VM as the value of every expression run
+ // we can inspect the results just by printing like so
+ println!("{:?}", output);
+}
+Types that implement IntoSteelVal
and FromSteelVal
and be returned and passed into rust functions, respectively. Take the following for example:
+#![allow(unused)] + +fn main() { +fn foo(value: isize) -> String { + ... +} + +}
This means that steel values will attempt to be coerced to the type in the function signature, and the value that this function returns will then attempt to be coerced into a that Steel understands.
+There are some special case conversions that happen specifically:
+IntoSteelVal
Ok(T)
then T else (error E)
Some(T)
then T else #false
Steel contains a limited form of the syntax-rules
that scheme provides. These macros build on the small primary language constructs that exist. Consider the following:
(define-syntax or
+ (syntax-rules ()
+ [(or) #f]
+ [(or x) x]
+ [(or x y) (let ([z x])
+ (if z z y))]
+ [(or x y ...) (or x (or y ...))]))
+
+(or #f #f #t)
+
+This will actually expand into something like this
+((λ (__z)
+ (if __z __z ((λ (__z) (if __z __z #t)) #f)))
+ #f)
+
+These macros allow for a simple extension of Steel to however you see fit - defining macros in terms of the syntax rules format is fairly straightforward.
+Inspired by Racket's higher order contracts, Steel
implements* higher order contracts to enable design by contract, made easy with a define/contract
macro for easier ergonomics. Racket makes use of a concept known as blame which seeks to identify the violating party - Steel
does not quite have fully fleshed out blame but that is a work in progress. Here are some examples:
;; Simple flat contracts
+(define/contract (test x y)
+ (->/c even? even? odd?)
+ (+ x y 1))
+
+(test 2 2) ;; => 5
+
+(define/contract (test-violation x y)
+ (->/c even? even? odd?)
+ (+ x y 1))
+
+(test-violation 1 2) ;; contract violation
+
+
+Contracts are implemented as values, so they are bound to functions. This enables the use of contract checking on functions themselves since functions can be passed around:
+;; Higher order contracts, check on application
+(define/contract (higher-order func y)
+ (->/c (->/c even? odd?) even? even?)
+ (+ 1 (func y)))
+
+(higher-order (lambda (x) (+ x 1)) 2) ;; => 4
+
+(define/contract (higher-order-violation func y)
+ (->/c (->/c even? odd?) even? even?)
+ (+ 1 (func y)))
+
+(higher-order-violation (lambda (x) (+ x 2)) 2) ;; contract violation
+
+Contracts on functions do not get checked until they are applied, so a function returning a contracted function won't cause a violation until that function is actually used:
+;; More higher order contracts, get checked on application
+(define/contract (output)
+ (->/c (->/c string? int?))
+ (lambda (x) 10))
+
+(define/contract (accept func)
+ (->/c (->/c string? int?) string?)
+ "cool cool cool")
+
+(accept (output)) ;; => "cool cool cool"
+
+;; different contracts on the argument
+(define/contract (accept-violation func)
+ (->/c (->/c string? string?) string?)
+ (func "applesauce")
+ "cool cool cool")
+
+(accept-violation (output)) ;; contract violation
+
+;; generates a function
+(define/contract (generate-closure)
+ (->/c (->/c string? int?))
+ (lambda (x) 10))
+
+;; calls generate-closure which should result in a contract violation
+(define/contract (accept-violation)
+ (->/c (->/c string? string?))
+ (generate-closure))
+
+((accept-violation) "test") ;; contract violation
+
+Perhaps a more nuanced case:
+(define/contract (output)
+ (->/c (->/c string? int?))
+ (lambda (x) 10.2))
+
+(define/contract (accept)
+ (->/c (->/c string? number?))
+ (output))
+
+
+((accept) "test") ;; contract violation 10.2 satisfies number? but _not_ int?
+
+Inspired by clojure's transducers, Steel
has a similar object that is somewhere half way in between transducers and iterators. Consider the following:
+(mapping (lambda (x) (+ x 1))) ;; => <#iterator>
+(filtering even?) ;; => <#iterator>
+(taking 15) ;; => <#iterator>
+
+(compose
+ (mapping add1)
+ (filtering odd?)
+ (taking 15)) ;; => <#iterator>
+
+Each of these expressions emit an <#iterator>
object, which means they're compatible with execute
and transduce
. Execute takes a transducer (i.e. <#iterator>
) and a collection that can be iterated (list
, vector
, or stream
) and applies the transducer.
;; Accepts lists
+(execute (mapping (lambda (x) (+ x 1))) (list 1 2 3 4 5)) ;; => '(2 3 4 5 6)
+
+;; Accepts vectors
+(execute (mapping (lambda (x) (+ x 1))) (vector 1 2 3 4 5)) ;; '#(2 3 4 5 6)
+
+;; Even accepts streams!
+(define (integers n)
+ (stream-cons n (lambda () (integers (+ 1 n)))))
+
+(execute (taking 5) (integers 0)) ;; => '(0 1 2 3 4)
+
+Transduce is just reduce
with more bells and whistles and works similarly:
;; (-> transducer reducing-function initial-value iterable)
+(transduce (mapping (lambda (x) (+ x 1))) + 0 (list 0 1 2 3)) ;; => 10
+
+Compose just combines the iterator functions and lets us avoid intermediate allocation. The composition works left to right - it chains each value through the functions and then accumulates into the output type. See the following:
+(define xf
+ (compose
+ (mapping add1)
+ (filtering odd?)
+ (taking 5)))
+
+(execute xf (range 0 100)) ;; => '(1 3 5 7 9)
+
+By default, execute outputs to the same type that was passed in. In other words, if you execute
a list
, it will return a list
. However, if you so choose, you can pass in a symbol specifying the output type of your choosing like so:
(define xf
+ (compose
+ (mapping add1)
+ (filtering odd?)
+ (taking 5)))
+
+;; Takes a list and returns a vector
+(execute xf (range 0 100) 'vector) ;; => '#(1 3 5 7 9)
+
+;; Takes a vector and returns a list
+(execute xf (vector 0 1 2 3 4 5 6 7 8 9 10) 'list) ;; => '(1 3 5 7 9)
+
+In order to support a growing codebase, Steel has module support for projects spanning multiple files. Steel files can provide
values (with contracts attached) and require
modules from other files:
;; main.stl
+(require "provide.stl")
+
+(even->odd 10)
+
+
+;; provide.stl
+(provide
+ (contract/out even->odd (->/c even? odd?))
+ no-contract
+ flat-value)
+
+(define (even->odd x)
+ (+ x 1))
+
+(define (accept-number x) (+ x 10))
+
+(define (no-contract) "cool cool cool")
+(define flat-value 15)
+
+(displayln "Calling even->odd with some bad inputs but its okay")
+(displayln (even->odd 1))
+
+Here we can see if we were to run main
that it would include the contents of provide
, and only provided values would be accessible from main
. The contract is attached at the contract boundary, so inside the provide
module, you can violate the contract, but outside the module the contract will be applied.
A few notes on modules:
+A
requires B
and C
, and B
requires C
, C
will be compiled once and shared between A
and B
.
+(keyword? v) -> boolean?
+
+v: any/c
+
+
+
+(mutable-vector args ...) -> mutable-vector?
+
+
+Macro for creating a new struct, in the form of:
+(struct <struct-name> (fields ...) options ...)
+The options can consist of the following:
Single variable options (those which their presence indicates #true
)
#:mutable
#:transparent
Other options must be presented as key value pairs, and will get stored
+in the struct instance. They will also be bound to the variable
+___<struct-name>-options___
in the same lexical environment where the
+struct was defined. For example:
λ > (struct Applesauce (a b c) #:mutable #:transparent #:unrecognized-option 1234)
+λ > ___Applesauce-options___
+=> #<hashmap {
+ '#:fields: '(a b c),
+ '#:mutable: #false,
+ '#:transparent: #false,
+ '#:unrecognized-option: 1234,
+}>
+
+By default, structs are immutable, which means setter functions will not +be generated. Also by default, structs are not transparent, which means +printing them will result in an opaque struct that does not list the fields
+(log! level arg-list)
+
+Log directly on the specified level the with arguments, as a list
+(log/info! . args)
+
+Log the arguments using the info target, i.e. log on INFO
+(log/warn! . args)
+
+Log the arguments using the warn target, i.e. log on WARN
+(log/debug! . args)
+
+Log the arguments using the debug target, i.e. log on DEBUG
+(log/error! . args)
+
+Log the arguments using the error target, i.e. log on ERROR
+(log/trace! . args)
+
+Log the arguments using the trace target, i.e. log on TRACE
+Gets the key
from the given map
. Returns #false if the key does not exist.
(hash-try-get map key) -> (or any/c #false)
+> (hash-try-get (hash 'a 10 'b 20) 'b) ;; => 20
+> (hash-try-get (hash 'a 10 'b 20) 'does-not-exist) ;; => #false
+
+Returns the number of key value pairs in the map
+(hash-length map) -> (and positive? int?)
+> (hash-length (hash 'a 10 'b 20)) ;; => 2
+
+Gets the key
from the given map
. Raises an error if the key does not exist. hash-get
is an alias for this.
(hash-ref map key) -> any/c
+> (hash-get (hash 'a 10 'b 20) 'b) ;; => 20
+
+Checks whether the given map contains the given key. Key must be hashable.
+(hash-contains? map key) -> bool?
+> (hash-contains? (hash 'a 10 'b 20) 'a) ;; => #true
+> (hash-contains? (hash 'a 10 'b 20) 'not-there) ;; => #false
+
+Returns a new hashmap with the additional key value pair added. Performs a functional update, +so the old hash map is still accessible.
+(hash-insert map key val) -> hash?
+> (hash-insert (hash 'a 10 'b 20) 'c 30)
+
+=> #<hashmap {
+'a: 10,
+'b: 20,
+'c: 30
+}>
+
+Contains direct wrappers around the Rust std::time::Instant
and std::time::Duration
modules.
+For example, to measure the time something takes:
(define t (instant/now))
+(displayln "Hello world")
+(displayln (instant/elapsed t))
+
+Takes a port and reads the entire content into a string
+(read-port-to-string port) -> string?
+Checks if a given value is an output port
+(output-port? any/c) -> bool?
+> (define output (open-output-file "foo.txt"))
+> (output-port? output) ;; => #true
+
+Takes a filename path
referring to an existing file and returns an input port. Raises an error
+if the file does not exist
(open-input-file string?) -> input-port?
+> (open-input-file "foo-bar.txt") ;; => #<port>
+> (open-input-file "file-does-not-exist.txt")
+error[E08]: Io
+┌─ :1:2
+│
+1 │ (open-input-file "foo-bar.txt")
+│ ^^^^^^^^^^^^^^^ No such file or directory (os error 2)
+
+Gets the port handle to stdin
+(stdin) -> input-port?
+> (stdin) ;; => #<port>
+
+Checks if a given value is an input port
+(input-port? any/c) -> bool?
+> (input-port? (stdin)) ;; => #true
+> (input-port? "foo") ;; => #false
+
+Takes a filename path
referring to a file to be created and returns an output port.
(open-output-file string?) -> output-port?
+> (open-output-file "foo-bar.txt") ;; => #<port>
+
+Checks if the input string starts with a prefix
+(starts-with? input pattern) -> bool?
+input : string? +pattern: string?
+> (starts-with? "foobar" "foo") ;; => #true
+> (starts-with? "foobar" "bar") ;; => #false
+
+Returns a list of strings from the original string split on the whitespace
+(split-whitespace string?) -> (listof string?)
+(split-whitespace "apples bananas fruits veggies") ;; '("apples" "bananas" "fruits" "veggies")
+
+Returns a new string with the trailing whitespace removed.
+(trim string?) -> string?
+> (trim " foo ") ;; => " foo"
+
+Converts a string into an int. Raises an error if the string cannot be converted to an integer.
+(string->int string?) -> int?
+> (string->int "100") ;; => 10
+> (string->int "not-an-int") ;; error
+
+Checks if the input string ends with a given suffix
+(ends-with? input pattern) -> bool?
+input : string? +pattern: string?
+> (ends-with? "foobar" "foo") ;; => #false
+> (ends-with? "foobar" "bar") ;; => #true
+
+Get the length of the given string
+(string-length string?) -> int?
+> (string-length "apples") ;; => 6
+
+Returns a new string with the leading whitespace removed.
+(trim string?) -> string?
+> (trim " foo ") ;; => "foo "
+
+Creates a new uppercased version of the input string
+(string->upper string?) -> string?
+> (string->upper "lower") ;; => "LOWER"
+
+Returns a new string with the leading and trailing whitespace removed.
+(trim string?) -> string?
+> (trim " foo ") ;; => "foo"
+
+Converts a string into a list of characters.
+(string->list string?) -> (listof char?)
+> (string->list "hello") ;; => '(#\h #\e #\l #\l #\o)
+
+Converts a string into a symbol.
+(string->symbol string?) -> symbol?
+> (string->symbol "FooBar") ;; => 'FooBar
+
+Converts an integer into a string.
+(int->string int?) -> string?
+> (int->string 10) ;; => "10"
+
+Concatenates all of the given strings into one
+(string-append strs...) -> string?
+> (string-append) ;; => ""
+> (string-append "foo" "bar") ;; => "foobar"
+### **string->lower**
+Creates a new lowercased version of the input string
+
+(string->lower string?) -> string?
+
+#### Examples
+
+```scheme
+> (string->lower "sPonGeBoB tExT") ;; => "spongebob text"
+
+Concatenatives all of the inputs to their string representation, separated by spaces.
+(to-string xs ...)
+> (to-string 10) ;; => "10"
+> (to-string "hello" "world") ;; => "hello world"
+
+Takes a port and reads the entire content into a string
+(read-port-to-string port) -> string?
+Checks whether the given map contains the given key. Key must be hashable.
+(hash-contains? map key) -> bool?
+> (hash-contains? (hash 'a 10 'b 20) 'a) ;; => #true
+> (hash-contains? (hash 'a 10 'b 20) 'not-there) ;; => #false
+
+Returns the value located at the given index. Will raise an error if you try to index out of bounds.
+Note: Runs in time proportional to the length of the list, however lists in Steel are implemented in such a fashion that the +time complexity is O(n/64). Meaning, for small lists this can be constant.
+(list-ref lst index) -> list?
+> (list-ref (list 1 2 3 4) 2) ;; => 3
+> (list-ref (range 0 100) 42) ;; => 42"
+> (list-ref (list 1 2 3 4) 10)
+error[E11]: Generic
+┌─ :1:2
+│
+1 │ (list-ref (list 1 2 3 4) 10)
+│ ^^^^^^^^ out of bounds index in list-ref - list length: 4, index: 10
+
+Get the second element of the list. Raises an error if the list does not have an element in the second position.
+(second l) -> any/c
+> (second '(1 2 3)) ;; => 2
+> (second '())
+error[E11]: Generic
+┌─ :1:2
+│
+1 │ (second '())
+│ ^^^^^^ second: index out of bounds - list did not have an element in the second position: []
+### **to-string**
+Concatenatives all of the inputs to their string representation, separated by spaces.
+
+(to-string xs ...)
+
+* xs : any/c
+
+#### Examples
+```scheme
+> (to-string 10) ;; => "10"
+> (to-string "hello" "world") ;; => "hello world"
+
+Checks if a given value is an output port
+(output-port? any/c) -> bool?
+> (define output (open-output-file "foo.txt"))
+> (output-port? output) ;; => #true
+
+Takes a filename path
referring to a file to be created and returns an output port.
(open-output-file string?) -> output-port?
+> (open-output-file "foo-bar.txt") ;; => #<port>
+
+Returns a newly allocated list of the elements in the range (n, m]
+(range n m) -> (listof int?)
+> (range 0 10) ;; => '(0 1 2 3 4 5 6 7 8 9)
+
+Get the length of the given string
+(string-length string?) -> int?
+> (string-length "apples") ;; => 6
+
+Returns the first element of the list l.
+(car l) -> any/c
+> (car '(1 2)) ;; => 1
+> (car (cons 2 3)) ;; => 2
+
+Gets the key
from the given map
. Raises an error if the key does not exist. hash-get
is an alias for this.
(hash-ref map key) -> any/c
+> (hash-get (hash 'a 10 'b 20) 'b) ;; => 20
+
+Returns the first element of the list l.
+(first l) -> any/c
+> (first '(1 2)) ;; => 1
+> (first (cons 2 3)) ;; => 2
+
+Returns a list of strings from the original string split on the whitespace
+(split-whitespace string?) -> (listof string?)
+(split-whitespace "apples bananas fruits veggies") ;; '("apples" "bananas" "fruits" "veggies")
+
+Returns a newly allocated list containing the vs as its elements.
+(list v ...) -> list?
+> (list 1 2 3 4 5) ;; => '(1 2 3 4 5)
+> (list (list 1 2) (list 3 4)) ;; => '((1 2) (3 4))
+
+Gets the key
from the given map
. Returns #false if the key does not exist.
(hash-try-get map key) -> (or any/c #false)
+> (hash-try-get (hash 'a 10 'b 20) 'b) ;; => 20
+> (hash-try-get (hash 'a 10 'b 20) 'does-not-exist) ;; => #false
+
+Checks if the given value can be treated as a pair. +Note - there are no improper lists in steel, so any list with at least one element +is considered a pair.
+(pair? any/c) -> bool?
+> (pair? '(10 20)) ;; => #true
+> (pair? '(10)) ;; => #true
+> (pair? '()) ;; => #false
+
+Lists in Steel have an interface that matches those of classic schemes or lisps. +At face value, they appear to be implemented as cons cells - however, under the hood +they are actually implemented as unrolled linked lists.
+This means that for most practical purposes, interaction with lists is the same. +That being said, there are no improper lists, meaning, pairs are actually just lists of two elements.
+Indexing into a list also takes O(n/64) - which means you'll get constant time indexing on small lists.
+(list 10 20 30 40) ;; => '(10 20 30 40)
+
+Get the third element of the list. Raises an error if the list does not have an element in the third position.
+(third l) -> any/c
+> (third '(1 2 3)) ;; => 3
+> (third '())
+error[E11]: Generic
+┌─ :1:2
+│
+1 │ (third '())
+│ ^^^^^^ third: index out of bounds - list did not have an element in the second position: []
+
+Returns the number of key value pairs in the map
+(hash-length map) -> (and positive? int?)
+> (hash-length (hash 'a 10 'b 20)) ;; => 2
+
+Creates a new uppercased version of the input string
+(string->upper string?) -> string?
+> (string->upper "lower") ;; => "LOWER"
+
+Returns a new string with the leading and trailing whitespace removed.
+(trim string?) -> string?
+> (trim " foo ") ;; => "foo"
+
+Checks if the input string starts with a prefix
+(starts-with? input pattern) -> bool?
+input : string? +pattern: string?
+> (starts-with? "foobar" "foo") ;; => #true
+> (starts-with? "foobar" "bar") ;; => #false
+
+Takes a filename path
referring to an existing file and returns an input port. Raises an error
+if the file does not exist
(open-input-file string?) -> input-port?
+> (open-input-file "foo-bar.txt") ;; => #<port>
+> (open-input-file "file-does-not-exist.txt")
+error[E08]: Io
+┌─ :1:2
+│
+1 │ (open-input-file "foo-bar.txt")
+│ ^^^^^^^^^^^^^^^ No such file or directory (os error 2)
+
+Checks if a given value is an input port
+(input-port? any/c) -> bool?
+> (input-port? (stdin)) ;; => #true
+> (input-port? "foo") ;; => #false
+
+Checks if the input string ends with a given suffix
+(ends-with? input pattern) -> bool?
+input : string? +pattern: string?
+> (ends-with? "foobar" "foo") ;; => #false
+> (ends-with? "foobar" "bar") ;; => #true
+
+Concatenates all of the given strings into one
+(string-append strs...) -> string?
+> (string-append) ;; => ""
+> (string-append "foo" "bar") ;; => "foobar"
+### **steel/time**
+
+
+#### steel/time
+
+Contains direct wrappers around the Rust `std::time::Instant` and `std::time::Duration` modules.
+For example, to measure the time something takes:
+
+```scheme
+(define t (instant/now))
+(displayln "Hello world")
+(displayln (instant/elapsed t))
+
+Returns a new string with the trailing whitespace removed.
+(trim string?) -> string?
+> (trim " foo ") ;; => " foo"
+
+Converts a string into a list of characters.
+(string->list string?) -> (listof char?)
+> (string->list "hello") ;; => '(#\h #\e #\l #\l #\o)
+
+Checks if the list is empty
+(empty? lst) -> bool?
+> (empty? (list 1 2 3 4 5)) ;; => #false
+> (empty? '()) ;; => #true
+
+Converts an integer into a string.
+(int->string int?) -> string?
+> (int->string 10) ;; => "10"
+
+Returns a new string with the leading whitespace removed.
+(trim string?) -> string?
+> (trim " foo ") ;; => "foo "
+
+Gets the port handle to stdin
+(stdin) -> input-port?
+> (stdin) ;; => #<port>
+
+Returns a new hashmap with the additional key value pair added. Performs a functional update, +so the old hash map is still accessible.
+(hash-insert map key val) -> hash?
+> (hash-insert (hash 'a 10 'b 20) 'c 30)
+
+=> #<hashmap {
+'a: 10,
+'b: 20,
+'c: 30
+}>
+
+Creates a new lowercased version of the input string
+(string->lower string?) -> string?
+> (string->lower "sPonGeBoB tExT") ;; => "spongebob text"
+
+Converts a string into an int. Raises an error if the string cannot be converted to an integer.
+(string->int string?) -> int?
+> (string->int "100") ;; => 10
+> (string->int "not-an-int") ;; error
+
+Converts a string into a symbol.
+(string->symbol string?) -> symbol?
+> (string->symbol "FooBar") ;; => 'FooBar
+
+Lists in Steel have an interface that matches those of classic schemes or lisps. +At face value, they appear to be implemented as cons cells - however, under the hood +they are actually implemented as unrolled linked lists.
+This means that for most practical purposes, interaction with lists is the same. +That being said, there are no improper lists, meaning, pairs are actually just lists of two elements.
+Indexing into a list also takes O(n/64) - which means you'll get constant time indexing on small lists.
+(list 10 20 30 40) ;; => '(10 20 30 40)
+
+Checks if the given value can be treated as a pair. +Note - there are no improper lists in steel, so any list with at least one element +is considered a pair.
+(pair? any/c) -> bool?
+> (pair? '(10 20)) ;; => #true
+> (pair? '(10)) ;; => #true
+> (pair? '()) ;; => #false
+
+Returns the first element of the list l.
+(car l) -> any/c
+> (car '(1 2)) ;; => 1
+> (car (cons 2 3)) ;; => 2
+
+Get the third element of the list. Raises an error if the list does not have an element in the third position.
+(third l) -> any/c
+> (third '(1 2 3)) ;; => 3
+> (third '())
+error[E11]: Generic
+┌─ :1:2
+│
+1 │ (third '())
+│ ^^^^^^ third: index out of bounds - list did not have an element in the second position: []
+
+Checks if the list is empty
+(empty? lst) -> bool?
+> (empty? (list 1 2 3 4 5)) ;; => #false
+> (empty? '()) ;; => #true
+
+Returns a newly allocated list of the elements in the range (n, m]
+(range n m) -> (listof int?)
+> (range 0 10) ;; => '(0 1 2 3 4 5 6 7 8 9)
+
+Returns the first element of the list l.
+(first l) -> any/c
+> (first '(1 2)) ;; => 1
+> (first (cons 2 3)) ;; => 2
+
+Returns a newly allocated list containing the vs as its elements.
+(list v ...) -> list?
+> (list 1 2 3 4 5) ;; => '(1 2 3 4 5)
+> (list (list 1 2) (list 3 4)) ;; => '((1 2) (3 4))
+
+Get the second element of the list. Raises an error if the list does not have an element in the second position.
+(second l) -> any/c
+> (second '(1 2 3)) ;; => 2
+> (second '())
+error[E11]: Generic
+┌─ :1:2
+│
+1 │ (second '())
+│ ^^^^^^ second: index out of bounds - list did not have an element in the second position: []
+### **list-ref**
+Returns the value located at the given index. Will raise an error if you try to index out of bounds.
+
+Note: Runs in time proportional to the length of the list, however lists in Steel are implemented in such a fashion that the
+time complexity is O(n/64). Meaning, for small lists this can be constant.
+
+(list-ref lst index) -> list?
+
+* lst : list?
+* index : (and/c int? positive?)
+
+#### Examples
+```scheme
+> (list-ref (list 1 2 3 4) 2) ;; => 3
+> (list-ref (range 0 100) 42) ;; => 42"
+> (list-ref (list 1 2 3 4) 10)
+error[E11]: Generic
+┌─ :1:2
+│
+1 │ (list-ref (list 1 2 3 4) 10)
+│ ^^^^^^^^ out of bounds index in list-ref - list length: 4, index: 10
+
+Returns the absolute value of the given input
+Returns the first element of the list l.
+(car l) -> any/c
+> (car '(1 2)) ;; => 1
+> (car (cons 2 3)) ;; => 2
+
+Checks if two characters are equal
+Requires that the two inputs are both characters, and will otherwise +raise an error.
+Recursively copies the directory from source to destination
+Creates the directory
+Check the current working directory
+Deletes the directory
+Checks if the list is empty
+(empty? lst) -> bool?
+> (empty? (list 1 2 3 4 5)) ;; => #false
+> (empty? '()) ;; => #true
+
+Checks if the input string ends with a given suffix
+(ends-with? input pattern) -> bool?
+input : string? +pattern: string?
+> (ends-with? "foobar" "foo") ;; => #false
+> (ends-with? "foobar" "bar") ;; => #true
+
+Returns Euler's number raised to the power of z.
+Gets the filename for a given path
+Returns the first element of the list l.
+(first l) -> any/c
+> (first '(1 2)) ;; => 1
+> (first (cons 2 3)) ;; => 2
+
+Checks whether the given map contains the given key. Key must be hashable.
+(hash-contains? map key) -> bool?
+> (hash-contains? (hash 'a 10 'b 20) 'a) ;; => #true
+> (hash-contains? (hash 'a 10 'b 20) 'not-there) ;; => #false
+
+Returns a new hashmap with the additional key value pair added. Performs a functional update, +so the old hash map is still accessible.
+(hash-insert map key val) -> hash?
+> (hash-insert (hash 'a 10 'b 20) 'c 30)
+
+=> #<hashmap {
+'a: 10,
+'b: 20,
+'c: 30
+}>
+
+Returns the keys of the given hash map as a list.
+(hash-keys->list map) -> (listof hashable?)
+
+> (hash-keys->list? (hash 'a 'b 20)) ;; => '(a b)
+
+Returns the number of key value pairs in the map
+(hash-length map) -> (and positive? int?)
+> (hash-length (hash 'a 10 'b 20)) ;; => 2
+
+Gets the key
from the given map
. Raises an error if the key does not exist. hash-get
is an alias for this.
(hash-ref map key) -> any/c
+> (hash-ref (hash 'a 10 'b 20) 'b) ;; => 20
+
+Gets the key
from the given map
. Returns #false if the key does not exist.
(hash-try-get map key) -> (or any/c #false)
+> (hash-try-get (hash 'a 10 'b 20) 'b) ;; => 20
+> (hash-try-get (hash 'a 10 'b 20) 'does-not-exist) ;; => #false
+
+Checks if a given value is an input port
+(input-port? any/c) -> bool?
+> (input-port? (stdin)) ;; => #true
+> (input-port? "foo") ;; => #false
+
+Converts an integer into a string.
+(int->string int?) -> string?
+> (int->string 10) ;; => "10"
+
+Checks if a path is a directory
+Checks if a path is a file
+Returns the length of the list.
+(length l) -> int?
+> (length (list 10 20 30)) ;; => 3
+
+Returns a newly allocated list containing the vs as its elements.
+(list v ...) -> list?
+> (list 1 2 3 4 5) ;; => '(1 2 3 4 5)
+> (list (list 1 2) (list 3 4)) ;; => '((1 2) (3 4))
+
+Returns the value located at the given index. Will raise an error if you try to index out of bounds.
+Note: Runs in time proportional to the length of the list, however lists in Steel are implemented in such a fashion that the +time complexity is O(n/64). Meaning, for small lists this can be constant.
+(list-ref lst index) -> list?
+> (list-ref (list 1 2 3 4) 2) ;; => 3
+> (list-ref (range 0 100) 42) ;; => 42"
+> (list-ref (list 1 2 3 4) 10)
+error[E11]: Generic
+┌─ :1:2
+│
+1 │ (list-ref (list 1 2 3 4) 10)
+│ ^^^^^^^^ out of bounds index in list-ref - list length: 4, index: 10
+
+Converts the given number to a string
+Takes a filename path
referring to an existing file and returns an input port. Raises an error
+if the file does not exist
(open-input-file string?) -> input-port?
+> (open-input-file "foo-bar.txt") ;; => #<port>
+> (open-input-file "file-does-not-exist.txt")
+error[E08]: Io
+┌─ :1:2
+│
+1 │ (open-input-file "foo-bar.txt")
+│ ^^^^^^^^^^^^^^^ No such file or directory (os error 2)
+
+Takes a filename path
referring to a file to be created and returns an output port.
(open-output-file string?) -> output-port?
+> (open-output-file "foo-bar.txt") ;; => #<port>
+
+Checks if a given value is an output port
+(output-port? any/c) -> bool?
+> (define output (open-output-file "foo.txt"))
+> (output-port? output) ;; => #true
+
+Checks if the given value can be treated as a pair. +Note - there are no improper lists in steel, so any list with at least one element +is considered a pair.
+(pair? any/c) -> bool?
+> (pair? '(10 20)) ;; => #true
+> (pair? '(10)) ;; => #true
+> (pair? '()) ;; => #false
+
+Gets the extension from a path
+Checks if a path exists
+Returns a newly allocated list of the elements in the range (n, m]
+(range n m) -> (listof int?)
+> (range 0 10) ;; => '(0 1 2 3 4 5 6 7 8 9)
+
+Returns the contents of the directory as a list
+Takes a port and reads the entire content into a string
+(read-port-to-string port) -> string?
+Get the second element of the list. Raises an error if the list does not have an element in the second position.
+(second l) -> any/c
+> (second '(1 2 3)) ;; => 2
+> (second '())
+error[E11]: Generic
+┌─ :1:2
+│
+1 │ (second '())
+│ ^^^^^^ second: index out of bounds - list did not have an element in the second position: []
+### **split-whitespace**
+Returns a list of strings from the original string split on the whitespace
+
+(split-whitespace string?) -> (listof string?)
+
+#### Examples
+
+```scheme
+(split-whitespace "apples bananas fruits veggies") ;; '("apples" "bananas" "fruits" "veggies")
+
+Checks if the input string starts with a prefix
+(starts-with? input pattern) -> bool?
+input : string? +pattern: string?
+> (starts-with? "foobar" "foo") ;; => #true
+> (starts-with? "foobar" "bar") ;; => #false
+
+Gets the port handle to stdin
+(stdin) -> input-port?
+> (stdin) ;; => #<port>
+
+Constructs a string from the given characters
+Converts a string into an int. Raises an error if the string cannot be converted to an integer.
+(string->int string?) -> int?
+> (string->int "100") ;; => 10
+> (string->int "not-an-int") ;; error
+
+Converts a string into a list of characters.
+(string->list string?) -> (listof char?)
+> (string->list "hello") ;; => '(#\h #\e #\l #\l #\o)
+
+Creates a new lowercased version of the input string
+(string->lower string?) -> string?
+> (string->lower "sPonGeBoB tExT") ;; => "spongebob text"
+
+Converts the given string to a number
+Converts a string into a symbol.
+(string->symbol string?) -> symbol?
+> (string->symbol "FooBar") ;; => 'FooBar
+
+Creates a new uppercased version of the input string
+(string->upper string?) -> string?
+> (string->upper "lower") ;; => "LOWER"
+
+Concatenates all of the given strings into one
+(string-append strs...) -> string?
+> (string-append) ;; => ""
+> (string-append "foo" "bar") ;; => "foobar"
+### **string-length**
+Get the length of the given string
+
+(string-length string?) -> int?
+
+#### Examples
+
+```scheme
+> (string-length "apples") ;; => 6
+
+Returns the first n elements of the list l as a new list.
+(take l n) -> list?
+> (take '(1 2 3 4) 2) ;; => '(0 1)
+> (take (range 0 10) 4) ;; => '(0 1 2 3)
+
+Get the third element of the list. Raises an error if the list does not have an element in the third position.
+(third l) -> any/c
+> (third '(1 2 3)) ;; => 3
+> (third '())
+error[E11]: Generic
+┌─ :1:2
+│
+1 │ (third '())
+│ ^^^^^^ third: index out of bounds - list did not have an element in the second position: []
+
+Concatenatives all of the inputs to their string representation, separated by spaces.
+(to-string xs ...)
+> (to-string 10) ;; => "10"
+> (to-string "hello" "world") ;; => "hello world"
+
+Returns a new string with the leading and trailing whitespace removed.
+(trim string?) -> string?
+> (trim " foo ") ;; => "foo"
+
+Returns a new string with the trailing whitespace removed.
+(trim string?) -> string?
+> (trim " foo ") ;; => " foo"
+
+Returns a new string with the given pat
repeatedly removed from the end
+of the string
(trim-end-matches string? string?) -> string?
+
+> (trim-end-matches "123foo1bar123123" "123") ;; => "123foo1bar"
+
+Returns a new string with the leading whitespace removed.
+(trim string?) -> string?
+> (trim " foo ") ;; => "foo "
+
+Returns a new string with the given pat
repeatedly removed from the start
+of the string
(trim-start-matches string? string?) -> string?
+
+> (trim-start-matches "123foo1bar123123" "123") ;; => "foo1bar123123"
+
+Lists in Steel have an interface that matches those of classic schemes or lisps. +At face value, they appear to be implemented as cons cells - however, under the hood +they are actually implemented as unrolled linked lists.
+This means that for most practical purposes, interaction with lists is the same. +That being said, there are no improper lists, meaning, pairs are actually just lists of two elements.
+Indexing into a list also takes O(n/64) - which means you'll get constant time indexing on small lists.
+(list 10 20 30 40) ;; => '(10 20 30 40)
+
+Returns the first element of the list l.
+(car l) -> any/c
+> (car '(1 2)) ;; => 1
+> (car (cons 2 3)) ;; => 2
+
+Checks if the list is empty
+(empty? lst) -> bool?
+> (empty? (list 1 2 3 4 5)) ;; => #false
+> (empty? '()) ;; => #true
+
+Returns the first element of the list l.
+(first l) -> any/c
+> (first '(1 2)) ;; => 1
+> (first (cons 2 3)) ;; => 2
+
+Returns the length of the list.
+(length l) -> int?
+> (length (list 10 20 30)) ;; => 3
+
+Returns a newly allocated list containing the vs as its elements.
+(list v ...) -> list?
+> (list 1 2 3 4 5) ;; => '(1 2 3 4 5)
+> (list (list 1 2) (list 3 4)) ;; => '((1 2) (3 4))
+
+Returns the value located at the given index. Will raise an error if you try to index out of bounds.
+Note: Runs in time proportional to the length of the list, however lists in Steel are implemented in such a fashion that the +time complexity is O(n/64). Meaning, for small lists this can be constant.
+(list-ref lst index) -> list?
+> (list-ref (list 1 2 3 4) 2) ;; => 3
+> (list-ref (range 0 100) 42) ;; => 42"
+> (list-ref (list 1 2 3 4) 10)
+error[E11]: Generic
+┌─ :1:2
+│
+1 │ (list-ref (list 1 2 3 4) 10)
+│ ^^^^^^^^ out of bounds index in list-ref - list length: 4, index: 10
+
+Checks if the given value can be treated as a pair. +Note - there are no improper lists in steel, so any list with at least one element +is considered a pair.
+(pair? any/c) -> bool?
+> (pair? '(10 20)) ;; => #true
+> (pair? '(10)) ;; => #true
+> (pair? '()) ;; => #false
+
+Returns a newly allocated list of the elements in the range (n, m]
+(range n m) -> (listof int?)
+> (range 0 10) ;; => '(0 1 2 3 4 5 6 7 8 9)
+
+Get the second element of the list. Raises an error if the list does not have an element in the second position.
+(second l) -> any/c
+> (second '(1 2 3)) ;; => 2
+> (second '())
+error[E11]: Generic
+┌─ :1:2
+│
+1 │ (second '())
+│ ^^^^^^ second: index out of bounds - list did not have an element in the second position: []
+### **take**
+Returns the first n elements of the list l as a new list.
+
+(take l n) -> list?
+
+* l : list?
+* n : (and/c positive? int?)
+
+#### Examples
+
+```scheme
+> (take '(1 2 3 4) 2) ;; => '(0 1)
+> (take (range 0 10) 4) ;; => '(0 1 2 3)
+
+Get the third element of the list. Raises an error if the list does not have an element in the third position.
+(third l) -> any/c
+> (third '(1 2 3)) ;; => 3
+> (third '())
+error[E11]: Generic
+┌─ :1:2
+│
+1 │ (third '())
+│ ^^^^^^ third: index out of bounds - list did not have an element in the second position: []
+
+Returns the absolute value of the given input
+Returns Euler's number raised to the power of z.
+Strings in Steel are immutable, fixed length arrays of characters. They are heap allocated,
+and are implemented under the hood as referenced counted rust Strings
.
Checks if two characters are equal
+Requires that the two inputs are both characters, and will otherwise +raise an error.
+Checks if the input string ends with a given suffix
+(ends-with? input pattern) -> bool?
+input : string? +pattern: string?
+> (ends-with? "foobar" "foo") ;; => #false
+> (ends-with? "foobar" "bar") ;; => #true
+
+Converts an integer into a string.
+(int->string int?) -> string?
+> (int->string 10) ;; => "10"
+
+Converts the given number to a string
+Returns a list of strings from the original string split on the whitespace
+(split-whitespace string?) -> (listof string?)
+(split-whitespace "apples bananas fruits veggies") ;; '("apples" "bananas" "fruits" "veggies")
+
+Checks if the input string starts with a prefix
+(starts-with? input pattern) -> bool?
+input : string? +pattern: string?
+> (starts-with? "foobar" "foo") ;; => #true
+> (starts-with? "foobar" "bar") ;; => #false
+
+Constructs a string from the given characters
+Converts a string into an int. Raises an error if the string cannot be converted to an integer.
+(string->int string?) -> int?
+> (string->int "100") ;; => 10
+> (string->int "not-an-int") ;; error
+
+Converts a string into a list of characters.
+(string->list string?) -> (listof char?)
+> (string->list "hello") ;; => '(#\h #\e #\l #\l #\o)
+
+Creates a new lowercased version of the input string
+(string->lower string?) -> string?
+> (string->lower "sPonGeBoB tExT") ;; => "spongebob text"
+
+Converts the given string to a number
+Converts a string into a symbol.
+(string->symbol string?) -> symbol?
+> (string->symbol "FooBar") ;; => 'FooBar
+
+Creates a new uppercased version of the input string
+(string->upper string?) -> string?
+> (string->upper "lower") ;; => "LOWER"
+
+Concatenates all of the given strings into one
+(string-append strs...) -> string?
+> (string-append) ;; => ""
+> (string-append "foo" "bar") ;; => "foobar"
+### **string-length**
+Get the length of the given string
+
+(string-length string?) -> int?
+
+#### Examples
+
+```scheme
+> (string-length "apples") ;; => 6
+
+Concatenatives all of the inputs to their string representation, separated by spaces.
+(to-string xs ...)
+> (to-string 10) ;; => "10"
+> (to-string "hello" "world") ;; => "hello world"
+
+Returns a new string with the leading and trailing whitespace removed.
+(trim string?) -> string?
+> (trim " foo ") ;; => "foo"
+
+Returns a new string with the trailing whitespace removed.
+(trim string?) -> string?
+> (trim " foo ") ;; => " foo"
+
+Returns a new string with the given pat
repeatedly removed from the end
+of the string
(trim-end-matches string? string?) -> string?
+
+> (trim-end-matches "123foo1bar123123" "123") ;; => "123foo1bar"
+
+Returns a new string with the leading whitespace removed.
+(trim string?) -> string?
+> (trim " foo ") ;; => "foo "
+
+Returns a new string with the given pat
repeatedly removed from the start
+of the string
(trim-start-matches string? string?) -> string?
+
+> (trim-start-matches "123foo1bar123123" "123") ;; => "foo1bar123123"
+
+Contains direct wrappers around the Rust std::time::Instant
and std::time::Duration
modules.
+For example, to measure the time something takes:
(define t (instant/now))
+(displayln "Hello world")
+(displayln (instant/elapsed t))
+
+Gets the key
from the given map
. Returns #false if the key does not exist.
(hash-try-get map key) -> (or any/c #false)
+> (hash-try-get (hash 'a 10 'b 20) 'b) ;; => 20
+> (hash-try-get (hash 'a 10 'b 20) 'does-not-exist) ;; => #false
+
+Returns the number of key value pairs in the map
+(hash-length map) -> (and positive? int?)
+> (hash-length (hash 'a 10 'b 20)) ;; => 2
+
+Gets the key
from the given map
. Raises an error if the key does not exist. hash-get
is an alias for this.
(hash-ref map key) -> any/c
+> (hash-get (hash 'a 10 'b 20) 'b) ;; => 20
+
+Checks whether the given map contains the given key. Key must be hashable.
+(hash-contains? map key) -> bool?
+> (hash-contains? (hash 'a 10 'b 20) 'a) ;; => #true
+> (hash-contains? (hash 'a 10 'b 20) 'not-there) ;; => #false
+
+Returns a new hashmap with the additional key value pair added. Performs a functional update, +so the old hash map is still accessible.
+(hash-insert map key val) -> hash?
+> (hash-insert (hash 'a 10 'b 20) 'c 30)
+
+=> #<hashmap {
+'a: 10,
+'b: 20,
+'c: 30
+}>
+
+Contains direct wrappers around the Rust std::time::Instant
and std::time::Duration
modules.
+For example, to measure the time something takes:
(define t (instant/now))
+(displayln "Hello world")
+(displayln (instant/elapsed t))
+
+Takes a port and reads the entire content into a string
+(read-port-to-string port) -> string?
+Checks if a given value is an output port
+(output-port? any/c) -> bool?
+> (define output (open-output-file "foo.txt"))
+> (output-port? output) ;; => #true
+
+Takes a filename path
referring to an existing file and returns an input port. Raises an error
+if the file does not exist
(open-input-file string?) -> input-port?
+> (open-input-file "foo-bar.txt") ;; => #<port>
+> (open-input-file "file-does-not-exist.txt")
+error[E08]: Io
+┌─ :1:2
+│
+1 │ (open-input-file "foo-bar.txt")
+│ ^^^^^^^^^^^^^^^ No such file or directory (os error 2)
+
+Gets the port handle to stdin
+(stdin) -> input-port?
+> (stdin) ;; => #<port>
+
+Checks if a given value is an input port
+(input-port? any/c) -> bool?
+> (input-port? (stdin)) ;; => #true
+> (input-port? "foo") ;; => #false
+
+Takes a filename path
referring to a file to be created and returns an output port.
(open-output-file string?) -> output-port?
+> (open-output-file "foo-bar.txt") ;; => #<port>
+
+Checks if the input string starts with a prefix
+(starts-with? input pattern) -> bool?
+input : string? +pattern: string?
+> (starts-with? "foobar" "foo") ;; => #true
+> (starts-with? "foobar" "bar") ;; => #false
+
+Returns a list of strings from the original string split on the whitespace
+(split-whitespace string?) -> (listof string?)
+(split-whitespace "apples bananas fruits veggies") ;; '("apples" "bananas" "fruits" "veggies")
+
+Returns a new string with the trailing whitespace removed.
+(trim string?) -> string?
+> (trim " foo ") ;; => " foo"
+
+Converts a string into an int. Raises an error if the string cannot be converted to an integer.
+(string->int string?) -> int?
+> (string->int "100") ;; => 10
+> (string->int "not-an-int") ;; error
+
+Checks if the input string ends with a given suffix
+(ends-with? input pattern) -> bool?
+input : string? +pattern: string?
+> (ends-with? "foobar" "foo") ;; => #false
+> (ends-with? "foobar" "bar") ;; => #true
+
+Get the length of the given string
+(string-length string?) -> int?
+> (string-length "apples") ;; => 6
+
+Returns a new string with the leading whitespace removed.
+(trim string?) -> string?
+> (trim " foo ") ;; => "foo "
+
+Creates a new uppercased version of the input string
+(string->upper string?) -> string?
+> (string->upper "lower") ;; => "LOWER"
+
+Returns a new string with the leading and trailing whitespace removed.
+(trim string?) -> string?
+> (trim " foo ") ;; => "foo"
+
+Converts a string into a list of characters.
+(string->list string?) -> (listof char?)
+> (string->list "hello") ;; => '(#\h #\e #\l #\l #\o)
+
+Converts a string into a symbol.
+(string->symbol string?) -> symbol?
+> (string->symbol "FooBar") ;; => 'FooBar
+
+Converts an integer into a string.
+(int->string int?) -> string?
+> (int->string 10) ;; => "10"
+
+Concatenates all of the given strings into one
+(string-append strs...) -> string?
+> (string-append) ;; => ""
+> (string-append "foo" "bar") ;; => "foobar"
+### **string->lower**
+Creates a new lowercased version of the input string
+
+(string->lower string?) -> string?
+
+#### Examples
+
+```scheme
+> (string->lower "sPonGeBoB tExT") ;; => "spongebob text"
+
+Concatenatives all of the inputs to their string representation, separated by spaces.
+(to-string xs ...)
+> (to-string 10) ;; => "10"
+> (to-string "hello" "world") ;; => "hello world"
+
+Takes a port and reads the entire content into a string
+(read-port-to-string port) -> string?
+Checks whether the given map contains the given key. Key must be hashable.
+(hash-contains? map key) -> bool?
+> (hash-contains? (hash 'a 10 'b 20) 'a) ;; => #true
+> (hash-contains? (hash 'a 10 'b 20) 'not-there) ;; => #false
+
+Returns the value located at the given index. Will raise an error if you try to index out of bounds.
+Note: Runs in time proportional to the length of the list, however lists in Steel are implemented in such a fashion that the +time complexity is O(n/64). Meaning, for small lists this can be constant.
+(list-ref lst index) -> list?
+> (list-ref (list 1 2 3 4) 2) ;; => 3
+> (list-ref (range 0 100) 42) ;; => 42"
+> (list-ref (list 1 2 3 4) 10)
+error[E11]: Generic
+┌─ :1:2
+│
+1 │ (list-ref (list 1 2 3 4) 10)
+│ ^^^^^^^^ out of bounds index in list-ref - list length: 4, index: 10
+
+Get the second element of the list. Raises an error if the list does not have an element in the second position.
+(second l) -> any/c
+> (second '(1 2 3)) ;; => 2
+> (second '())
+error[E11]: Generic
+┌─ :1:2
+│
+1 │ (second '())
+│ ^^^^^^ second: index out of bounds - list did not have an element in the second position: []
+### **to-string**
+Concatenatives all of the inputs to their string representation, separated by spaces.
+
+(to-string xs ...)
+
+* xs : any/c
+
+#### Examples
+```scheme
+> (to-string 10) ;; => "10"
+> (to-string "hello" "world") ;; => "hello world"
+
+Checks if a given value is an output port
+(output-port? any/c) -> bool?
+> (define output (open-output-file "foo.txt"))
+> (output-port? output) ;; => #true
+
+Takes a filename path
referring to a file to be created and returns an output port.
(open-output-file string?) -> output-port?
+> (open-output-file "foo-bar.txt") ;; => #<port>
+
+Returns a newly allocated list of the elements in the range (n, m]
+(range n m) -> (listof int?)
+> (range 0 10) ;; => '(0 1 2 3 4 5 6 7 8 9)
+
+Get the length of the given string
+(string-length string?) -> int?
+> (string-length "apples") ;; => 6
+
+Returns the first element of the list l.
+(car l) -> any/c
+> (car '(1 2)) ;; => 1
+> (car (cons 2 3)) ;; => 2
+
+Gets the key
from the given map
. Raises an error if the key does not exist. hash-get
is an alias for this.
(hash-ref map key) -> any/c
+> (hash-get (hash 'a 10 'b 20) 'b) ;; => 20
+
+Returns the first element of the list l.
+(first l) -> any/c
+> (first '(1 2)) ;; => 1
+> (first (cons 2 3)) ;; => 2
+
+Returns a list of strings from the original string split on the whitespace
+(split-whitespace string?) -> (listof string?)
+(split-whitespace "apples bananas fruits veggies") ;; '("apples" "bananas" "fruits" "veggies")
+
+Returns a newly allocated list containing the vs as its elements.
+(list v ...) -> list?
+> (list 1 2 3 4 5) ;; => '(1 2 3 4 5)
+> (list (list 1 2) (list 3 4)) ;; => '((1 2) (3 4))
+
+Gets the key
from the given map
. Returns #false if the key does not exist.
(hash-try-get map key) -> (or any/c #false)
+> (hash-try-get (hash 'a 10 'b 20) 'b) ;; => 20
+> (hash-try-get (hash 'a 10 'b 20) 'does-not-exist) ;; => #false
+
+Checks if the given value can be treated as a pair. +Note - there are no improper lists in steel, so any list with at least one element +is considered a pair.
+(pair? any/c) -> bool?
+> (pair? '(10 20)) ;; => #true
+> (pair? '(10)) ;; => #true
+> (pair? '()) ;; => #false
+
+Lists in Steel have an interface that matches those of classic schemes or lisps. +At face value, they appear to be implemented as cons cells - however, under the hood +they are actually implemented as unrolled linked lists.
+This means that for most practical purposes, interaction with lists is the same. +That being said, there are no improper lists, meaning, pairs are actually just lists of two elements.
+Indexing into a list also takes O(n/64) - which means you'll get constant time indexing on small lists.
+(list 10 20 30 40) ;; => '(10 20 30 40)
+
+Get the third element of the list. Raises an error if the list does not have an element in the third position.
+(third l) -> any/c
+> (third '(1 2 3)) ;; => 3
+> (third '())
+error[E11]: Generic
+┌─ :1:2
+│
+1 │ (third '())
+│ ^^^^^^ third: index out of bounds - list did not have an element in the second position: []
+
+Returns the number of key value pairs in the map
+(hash-length map) -> (and positive? int?)
+> (hash-length (hash 'a 10 'b 20)) ;; => 2
+
+Creates a new uppercased version of the input string
+(string->upper string?) -> string?
+> (string->upper "lower") ;; => "LOWER"
+
+Returns a new string with the leading and trailing whitespace removed.
+(trim string?) -> string?
+> (trim " foo ") ;; => "foo"
+
+Checks if the input string starts with a prefix
+(starts-with? input pattern) -> bool?
+input : string? +pattern: string?
+> (starts-with? "foobar" "foo") ;; => #true
+> (starts-with? "foobar" "bar") ;; => #false
+
+Takes a filename path
referring to an existing file and returns an input port. Raises an error
+if the file does not exist
(open-input-file string?) -> input-port?
+> (open-input-file "foo-bar.txt") ;; => #<port>
+> (open-input-file "file-does-not-exist.txt")
+error[E08]: Io
+┌─ :1:2
+│
+1 │ (open-input-file "foo-bar.txt")
+│ ^^^^^^^^^^^^^^^ No such file or directory (os error 2)
+
+Checks if a given value is an input port
+(input-port? any/c) -> bool?
+> (input-port? (stdin)) ;; => #true
+> (input-port? "foo") ;; => #false
+
+Checks if the input string ends with a given suffix
+(ends-with? input pattern) -> bool?
+input : string? +pattern: string?
+> (ends-with? "foobar" "foo") ;; => #false
+> (ends-with? "foobar" "bar") ;; => #true
+
+Concatenates all of the given strings into one
+(string-append strs...) -> string?
+> (string-append) ;; => ""
+> (string-append "foo" "bar") ;; => "foobar"
+### **steel/time**
+
+
+#### steel/time
+
+Contains direct wrappers around the Rust `std::time::Instant` and `std::time::Duration` modules.
+For example, to measure the time something takes:
+
+```scheme
+(define t (instant/now))
+(displayln "Hello world")
+(displayln (instant/elapsed t))
+
+Returns a new string with the trailing whitespace removed.
+(trim string?) -> string?
+> (trim " foo ") ;; => " foo"
+
+Converts a string into a list of characters.
+(string->list string?) -> (listof char?)
+> (string->list "hello") ;; => '(#\h #\e #\l #\l #\o)
+
+Checks if the list is empty
+(empty? lst) -> bool?
+> (empty? (list 1 2 3 4 5)) ;; => #false
+> (empty? '()) ;; => #true
+
+Converts an integer into a string.
+(int->string int?) -> string?
+> (int->string 10) ;; => "10"
+
+Returns a new string with the leading whitespace removed.
+(trim string?) -> string?
+> (trim " foo ") ;; => "foo "
+
+Gets the port handle to stdin
+(stdin) -> input-port?
+> (stdin) ;; => #<port>
+
+Returns a new hashmap with the additional key value pair added. Performs a functional update, +so the old hash map is still accessible.
+(hash-insert map key val) -> hash?
+> (hash-insert (hash 'a 10 'b 20) 'c 30)
+
+=> #<hashmap {
+'a: 10,
+'b: 20,
+'c: 30
+}>
+
+Creates a new lowercased version of the input string
+(string->lower string?) -> string?
+> (string->lower "sPonGeBoB tExT") ;; => "spongebob text"
+
+Converts a string into an int. Raises an error if the string cannot be converted to an integer.
+(string->int string?) -> int?
+> (string->int "100") ;; => 10
+> (string->int "not-an-int") ;; error
+
+Converts a string into a symbol.
+(string->symbol string?) -> symbol?
+> (string->symbol "FooBar") ;; => 'FooBar
+
+Lists in Steel have an interface that matches those of classic schemes or lisps. +At face value, they appear to be implemented as cons cells - however, under the hood +they are actually implemented as unrolled linked lists.
+This means that for most practical purposes, interaction with lists is the same. +That being said, there are no improper lists, meaning, pairs are actually just lists of two elements.
+Indexing into a list also takes O(n/64) - which means you'll get constant time indexing on small lists.
+(list 10 20 30 40) ;; => '(10 20 30 40)
+
+Checks if the given value can be treated as a pair. +Note - there are no improper lists in steel, so any list with at least one element +is considered a pair.
+(pair? any/c) -> bool?
+> (pair? '(10 20)) ;; => #true
+> (pair? '(10)) ;; => #true
+> (pair? '()) ;; => #false
+
+Returns the first element of the list l.
+(car l) -> any/c
+> (car '(1 2)) ;; => 1
+> (car (cons 2 3)) ;; => 2
+
+Get the third element of the list. Raises an error if the list does not have an element in the third position.
+(third l) -> any/c
+> (third '(1 2 3)) ;; => 3
+> (third '())
+error[E11]: Generic
+┌─ :1:2
+│
+1 │ (third '())
+│ ^^^^^^ third: index out of bounds - list did not have an element in the second position: []
+
+Checks if the list is empty
+(empty? lst) -> bool?
+> (empty? (list 1 2 3 4 5)) ;; => #false
+> (empty? '()) ;; => #true
+
+Returns a newly allocated list of the elements in the range (n, m]
+(range n m) -> (listof int?)
+> (range 0 10) ;; => '(0 1 2 3 4 5 6 7 8 9)
+
+Returns the first element of the list l.
+(first l) -> any/c
+> (first '(1 2)) ;; => 1
+> (first (cons 2 3)) ;; => 2
+
+Returns a newly allocated list containing the vs as its elements.
+(list v ...) -> list?
+> (list 1 2 3 4 5) ;; => '(1 2 3 4 5)
+> (list (list 1 2) (list 3 4)) ;; => '((1 2) (3 4))
+
+Get the second element of the list. Raises an error if the list does not have an element in the second position.
+(second l) -> any/c
+> (second '(1 2 3)) ;; => 2
+> (second '())
+error[E11]: Generic
+┌─ :1:2
+│
+1 │ (second '())
+│ ^^^^^^ second: index out of bounds - list did not have an element in the second position: []
+### **list-ref**
+Returns the value located at the given index. Will raise an error if you try to index out of bounds.
+
+Note: Runs in time proportional to the length of the list, however lists in Steel are implemented in such a fashion that the
+time complexity is O(n/64). Meaning, for small lists this can be constant.
+
+(list-ref lst index) -> list?
+
+* lst : list?
+* index : (and/c int? positive?)
+
+#### Examples
+```scheme
+> (list-ref (list 1 2 3 4) 2) ;; => 3
+> (list-ref (range 0 100) 42) ;; => 42"
+> (list-ref (list 1 2 3 4) 10)
+error[E11]: Generic
+┌─ :1:2
+│
+1 │ (list-ref (list 1 2 3 4) 10)
+│ ^^^^^^^^ out of bounds index in list-ref - list length: 4, index: 10
+
+Inspired by Racket's higher order contracts, Steel
implements* higher order contracts to enable design by contract, made easy with a define/contract
macro for easier ergonomics. Racket makes use of a concept known as blame which seeks to identify the violating party - Steel
does not quite have fully fleshed out blame but that is a work in progress. Here are some examples:
;; Simple flat contracts
+(define/contract (test x y)
+ (->/c even? even? odd?)
+ (+ x y 1))
+
+(test 2 2) ;; => 5
+
+(define/contract (test-violation x y)
+ (->/c even? even? odd?)
+ (+ x y 1))
+
+(test-violation 1 2) ;; contract violation
+
+
+Contracts are implemented as values, so they are bound to functions. This enables the use of contract checking on functions themselves since functions can be passed around:
+;; Higher order contracts, check on application
+(define/contract (higher-order func y)
+ (->/c (->/c even? odd?) even? even?)
+ (+ 1 (func y)))
+
+(higher-order (lambda (x) (+ x 1)) 2) ;; => 4
+
+(define/contract (higher-order-violation func y)
+ (->/c (->/c even? odd?) even? even?)
+ (+ 1 (func y)))
+
+(higher-order-violation (lambda (x) (+ x 2)) 2) ;; contract violation
+
+Contracts on functions do not get checked until they are applied, so a function returning a contracted function won't cause a violation until that function is actually used:
+;; More higher order contracts, get checked on application
+(define/contract (output)
+ (->/c (->/c string? int?))
+ (lambda (x) 10))
+
+(define/contract (accept func)
+ (->/c (->/c string? int?) string?)
+ "cool cool cool")
+
+(accept (output)) ;; => "cool cool cool"
+
+;; different contracts on the argument
+(define/contract (accept-violation func)
+ (->/c (->/c string? string?) string?)
+ (func "applesauce")
+ "cool cool cool")
+
+(accept-violation (output)) ;; contract violation
+
+;; generates a function
+(define/contract (generate-closure)
+ (->/c (->/c string? int?))
+ (lambda (x) 10))
+
+;; calls generate-closure which should result in a contract violation
+(define/contract (accept-violation)
+ (->/c (->/c string? string?))
+ (generate-closure))
+
+((accept-violation) "test") ;; contract violation
+
+Perhaps a more nuanced case:
+(define/contract (output)
+ (->/c (->/c string? int?))
+ (lambda (x) 10.2))
+
+(define/contract (accept)
+ (->/c (->/c string? number?))
+ (output))
+
+
+((accept) "test") ;; contract violation 10.2 satisfies number? but _not_ int?
+
+
+
+(keyword? v) -> boolean?
+
+v: any/c
+
+
+
+(mutable-vector args ...) -> mutable-vector?
+
+
+Macro for creating a new struct, in the form of:
+(struct <struct-name> (fields ...) options ...)
+The options can consist of the following:
Single variable options (those which their presence indicates #true
)
#:mutable
#:transparent
Other options must be presented as key value pairs, and will get stored
+in the struct instance. They will also be bound to the variable
+___<struct-name>-options___
in the same lexical environment where the
+struct was defined. For example:
λ > (struct Applesauce (a b c) #:mutable #:transparent #:unrecognized-option 1234)
+λ > ___Applesauce-options___
+=> #<hashmap {
+ '#:fields: '(a b c),
+ '#:mutable: #false,
+ '#:transparent: #false,
+ '#:unrecognized-option: 1234,
+}>
+
+By default, structs are immutable, which means setter functions will not +be generated. Also by default, structs are not transparent, which means +printing them will result in an opaque struct that does not list the fields
+ +Steel contains a limited form of the syntax-rules
that scheme provides. These macros build on the small primary language constructs that exist. Consider the following:
(define-syntax or
+ (syntax-rules ()
+ [(or) #f]
+ [(or x) x]
+ [(or x y) (let ([z x])
+ (if z z y))]
+ [(or x y ...) (or x (or y ...))]))
+
+(or #f #f #t)
+
+This will actually expand into something like this
+((λ (__z)
+ (if __z __z ((λ (__z) (if __z __z #t)) #f)))
+ #f)
+
+These macros allow for a simple extension of Steel to however you see fit - defining macros in terms of the syntax rules format is fairly straightforward.
+ +In order to support a growing codebase, Steel has module support for projects spanning multiple files. Steel files can provide
values (with contracts attached) and require
modules from other files:
;; main.stl
+(require "provide.stl")
+
+(even->odd 10)
+
+
+;; provide.stl
+(provide
+ (contract/out even->odd (->/c even? odd?))
+ no-contract
+ flat-value)
+
+(define (even->odd x)
+ (+ x 1))
+
+(define (accept-number x) (+ x 10))
+
+(define (no-contract) "cool cool cool")
+(define flat-value 15)
+
+(displayln "Calling even->odd with some bad inputs but its okay")
+(displayln (even->odd 1))
+
+Here we can see if we were to run main
that it would include the contents of provide
, and only provided values would be accessible from main
. The contract is attached at the contract boundary, so inside the provide
module, you can violate the contract, but outside the module the contract will be applied.
A few notes on modules:
+A
requires B
and C
, and B
requires C
, C
will be compiled once and shared between A
and B
. Inspired by clojure's transducers, Steel
has a similar object that is somewhere half way in between transducers and iterators. Consider the following:
+(mapping (lambda (x) (+ x 1))) ;; => <#iterator>
+(filtering even?) ;; => <#iterator>
+(taking 15) ;; => <#iterator>
+
+(compose
+ (mapping add1)
+ (filtering odd?)
+ (taking 15)) ;; => <#iterator>
+
+Each of these expressions emit an <#iterator>
object, which means they're compatible with execute
and transduce
. Execute takes a transducer (i.e. <#iterator>
) and a collection that can be iterated (list
, vector
, or stream
) and applies the transducer.
;; Accepts lists
+(execute (mapping (lambda (x) (+ x 1))) (list 1 2 3 4 5)) ;; => '(2 3 4 5 6)
+
+;; Accepts vectors
+(execute (mapping (lambda (x) (+ x 1))) (vector 1 2 3 4 5)) ;; '#(2 3 4 5 6)
+
+;; Even accepts streams!
+(define (integers n)
+ (stream-cons n (lambda () (integers (+ 1 n)))))
+
+(execute (taking 5) (integers 0)) ;; => '(0 1 2 3 4)
+
+Transduce is just reduce
with more bells and whistles and works similarly:
;; (-> transducer reducing-function initial-value iterable)
+(transduce (mapping (lambda (x) (+ x 1))) + 0 (list 0 1 2 3)) ;; => 10
+
+Compose just combines the iterator functions and lets us avoid intermediate allocation. The composition works left to right - it chains each value through the functions and then accumulates into the output type. See the following:
+(define xf
+ (compose
+ (mapping add1)
+ (filtering odd?)
+ (taking 5)))
+
+(execute xf (range 0 100)) ;; => '(1 3 5 7 9)
+
+By default, execute outputs to the same type that was passed in. In other words, if you execute
a list
, it will return a list
. However, if you so choose, you can pass in a symbol specifying the output type of your choosing like so:
(define xf
+ (compose
+ (mapping add1)
+ (filtering odd?)
+ (taking 5)))
+
+;; Takes a list and returns a vector
+(execute xf (range 0 100) 'vector) ;; => '#(1 3 5 7 9)
+
+;; Takes a vector and returns a list
+(execute xf (vector 0 1 2 3 4 5 6 7 8 9 10) 'list) ;; => '(1 3 5 7 9)
+
+
+