From: Brendan Hansen Date: Fri, 10 Dec 2021 13:35:31 +0000 (-0600) Subject: added some examples X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=1330478699c33b77d12e1dece4051f2752debcc5;p=onyx.git added some examples --- diff --git a/examples/19_do_blocks.onyx b/examples/19_do_blocks.onyx index d1f648b4..80631ecb 100644 --- a/examples/19_do_blocks.onyx +++ b/examples/19_do_blocks.onyx @@ -1,9 +1,69 @@ -// Unfinished Example +// "do"-blocks are a tiny feature found in some programming languages that you might find +// useful in certain circumstances. They allow you to write a block of code in an expression, +// and make the type inference system in Onyx even more powerful. -#load "core/std" +main :: (args: [] cstr) { + // Do-blocks are written like so. As you can probabily infer, + // the do-block allows you to write any statements you normally + // would in a block, except in an expression. As you can also + // see to specify the value that the do-block becomes, you + // write a "return" statement. The type of thing(s) being returned + // dictates the type of the do-block. + x := do { + a := 10; + b := 5; + return a * b; + }; -use package core + printf("x is a {} with value {}.\n", typeof x, x); -main :: (args: [] cstr) { - + + // There can be multiple return statements, in which case the + // first lexical return statement takes precedence over the rest, + // and provides the type. This is the same rule as auto-returned-typed + // which will be discussed in a later example. + y := do { + if x <= 10 do return 10.0f; + return 40; // This will cast to a f32, because the first return + // was of type f32. + }; + + printf("y is a {} with value {}.\n", typeof y, y); + + + // In all honesty, I barely use this feature of the language because + // I forget that it exists. It isn't something that is very useful, + // and can lead to some code-smells, if do-blocks are used in strange + // places, like as arguments. However, during the Advent of Code 2021 + // I had a real use-case for them and it looked like this: + +#if false { + left, right := do { + parts := string.split(line, #char "|"); + defer memory.free_slice(^parts); + return parts[0], parts[1]; + }; } + + // I had a string that I wanted to split into two parts, but wanted to + // free the resulting array, as I only needed the first two results. + // I could have written it like this instead: + +#if false { + left, right: str; + { + parts := string.split(line, #char "|"); + left, right = parts[0], parts[1]; + memory.free_slice(^parts); + } +} + + // As you can see, a normal block would have worked as well. Except, + // because left and right need to be declared outside that block, I + // would have had to explicitly type them. By using a do-block I did + // not have to give them a type, and got to free the memory. + +} + +#load "core/std" +use package core diff --git a/examples/20_auto_return.onyx b/examples/20_auto_return.onyx index d1f648b4..5a52db09 100644 --- a/examples/20_auto_return.onyx +++ b/examples/20_auto_return.onyx @@ -1,9 +1,47 @@ -// Unfinished Example +// Sometimes it can be a lot to explicitly write out the +// return type of a function. In fact, there are times in +// Onyx where it is actually impossible to write the return +// type of a function, as you'll see later. -#load "core/std" +main :: (args: [] cstr) { + // If you don't want to explicitly write the return type + // of a function, you can use #auto: + f :: (x: i32) -> #auto { + return x * 2; + } -use package core + y := f(10); + printf("Y({}): {}\n", typeof y, y); -main :: (args: [] cstr) { - + // That's really all you need to know... It is just a + // convinent shorthand. However, like I said, there are + // some cases where it is impossible to actually write + // the type so #auto is the only solution. Take this + // example. It takes something that is iterable, and + // returns an array of whatever that iterable iterates + // over. Because the only way to pattern match the V + // variable is by using it as a parameter, there is an + // inner macro that serves that purpose. It would be + // impossible to write the return type of consume, because + // it would be "[] V", but where would the V variable + // come from? The only way to do this is use an auto-return + // type and let the compiler fill it in. + + consume :: (it: $T) -> #auto where iter.Iterable(T) { + consume_inner :: macro (it: Iterator($V)) -> [] V { + arr: [..] V; + for v: it do arr << v; + return arr; + } + + return consume_inner(iter.as_iterator(it)); + } + + + arr := consume(range.{ 10, 1, -1 }); + println(arr); } + +#load "core/std" +use package core +