Honcho/Sample code
From Halfgeek KB
Sample concept code for honcho.
Code
Translations from existing code
- Honcho/Sample code/LocationSensitiveDemo (from Java)
- Honcho/Sample code/Scala Swing Dialogs Demo
- Honcho/Sample code/Rosetta Code Higher Order Functions
Hello (Java host)
$Array : resolve dits Array $String : resolve dits String $ArrayOfString : generic $Array $String $System : resolve java lang System // "resolve" without parameters indicates the nameless namespace. $global : resolve // We define an object instead of a class since we're defining what is // traditionally a static method. $Hello : object $tc : control : withParams // Since "$this" is never used, the target register is optional. // However, the rest is still necessary to make the method signatures // correct. $this : param $Hello $main : block // Similarly to "$this" above, the target register is optional // here. $args : param $ArrayOfString // A ":get" suffix is a dits naming convention for an accessor to a // member. ":set" is used in a similar way for mutators. $out = out:get $System = println $out "Hello World!" : done // Specifies that the above method can be accessed by the "main" method // of the object. = define $tc main $main : done : done // Specifies that the object defined above is the type interface object of the // name "Hello" in the global namespace. = define $global Hello $Hello
Hello (Java host with alternate startup convention)
Note the following:
- The block directive can be used to define a dits.Function object at compile time. If it's treated like an object as opposed to just a method, it's possible to call it as a function using its "apply" operator.
- If the program starter cannot locate a
void main(String[])method on the startup type's type object, it should fall back tovoid apply(String[])when available.
If these points are implemented, it is possible to define a program in a single function without a wrapper class. The following would thus function identically to the above.
$Array : resolve dits Array $String : resolve dits String $ArrayOfString : generic $Array $String $System : resolve java lang System $global : resolve $main : block $args : param $ArrayOfString $out = out:get $System = println $out "Hello World!" : done = define $global Hello $main
While in terms of loop
$functionType : resolve dits Function $booleanType : resolve dits Boolean $unitType : resolve dits Unit // Block that returns boolean, has no args $booleanBlockType : generic $functionType $booleanType // Block that returns nothing, has no args $unitBlockType : generic $functionType $unitType $while : block $here : control $conditionalBlock : param $booleanBlockType $successBlock : param $unitBlockType : returns // inferred $combined : block // obscure previous control $here : control : returns // inferred $t = apply $conditionalBlock $s = if $t $successBlock = return $here $s : done $r = loop $combined = return $here $r : done
While in terms of block operations
A strong argument for only including either block jumps or special boolean block operations.
$functionType : resolve dits Function $booleanType : resolve dits Boolean $unitType : resolve dits Unit // Block that returns boolean, has no args $booleanBlockType : generic $functionType $booleanType // Block that returns nothing, has no args $unitBlockType : generic $functionType $unitType $while : block $here : control $conditionalBlock : param $booleanBlockType $successBlock : param $unitBlockType : returns // inferred $t = apply $conditionalBlock = returnIfNot $here $t $t = apply $successBlock = restart $here : done
The returnIfNot operator above could be expanded to the following equivalent:
... $return_t : block = return $here $t : done $not_t = not $t = if $not_t $return_t ...
Simple closure example
In JavaScript:
var funcs = []; function print(s) { // Replace with the equivalent on the host platform. console.log(s); } function createFunc(i) { return function() { print("My value: " + i); }; } for(var i = 0; i < 3; ++i) { funcs[i] = createFunc(i); } for(var j = 0; j < 3; ++j) { funcs[j](); }
In Honcho:
$int : resolve dits Int $string : resolve dits String $array : resolve dits Array $main : block // This print closure should be replaced with the host platform's // equivalent. $print : block $s : param $string $system : resolve java lang System $out = out:get $system = println $out $s : done // The inferred return type of this function would be something like // dits.Function[dits.Unit]. $createFunc : block $here : control : returns // infer $i : param $int $created : block $cat = + "My value: " $i = apply $print $cat : done = return $here $created : done $createFuncType : typeOf $createFunc // = type dits.Function[dits.Function[dits.Unit]] $SKIP $createdFuncType : deconstruct $createFuncType // = type dits.Function[dits.Unit] $arrayOfCreated : generic $array $createdFuncType // = type dits.Array[dits.Function[dits.Unit]] $funcs = instance $arrayOfCreated 3 // = dits.Array[dits.Function[dits.Unit]].instance(3) // We make use of the "next" directive here to avoid using mutable // iterators in the for loop. Consider this a weird hybrid of recursion and // iteration. // 0 is the initial value of $i : run 0 $here : control $i : param $int // while $i < 3 $test = < $i 3 = returnIfNot $here $test $fn = apply $createFunc $i // The "update" operator sets an array index as in Scala = update $funcs $i $fn // Increment the counter and then use next to tail call $i1 = + $i 1 = next $here $i1 : done // Almost the same as the previous loop. : run 0 $here : control $j : param $int $test = < $j 3 = returnIfNot $here $test // The apply operator indexes into an array as in Scala $fn = apply $funcs $j = apply $j $j1 = + $j 1 = next $here $j1 : done : done
Trait to approximate Java StringBuilder/StringBuffer
What follows is potential syntax for defining a trait (mixin+interface) in this language. Note the following:
- The trait is defined in a block-like container using the word "trait" in place of "block".
- A "define" directive is used to attach locally-defined structures to the global type system. This is done both with methods (which are attached to the trait) and the trait itself (which is attached to a package).
- In the context of an object method, $this is a reserved register and has the intuitive meaning.
- An "abstract" directive defines an abstract block, which defines everything about a block except its implementation. The block cannot contain operators.
- Blocks defined within a "withParams" have additional parameters prepended. In the example below, every function has an additional "$this" parameter prepended. This is probably how "this" pointers will be implemented.
- Even though values such as numbers and characters are not necessarily objects, they always have access to certain operators such as the arithmetic operators and toString.
- A trait can demand that the object it helps implement defines certain other interfaces.
// vim:syntax=verilog // No, this is not verilog, but for what it's worth verilog syntax coloring is // a decent approximation of what it should be for this language. // This wiki now uses a rudimentary GeSHi colorizer for honcho. // Some of these resolves are almost certainly wrong, but this is just a // mockup. $Array : resolve dits Array $Character : resolve java lang Character $CharSequence : resolve java lang CharSequence $Object : resolve java lang Object $String : resolve java lang String $StringBuffer : resolve java lang StringBuffer $boolean : resolve dits Boolean $char : resolve dits Char16 $charArray : generic $Array $char $double : resolve dits Float64 $float : resolve dits Float32 $int : resolve dits Int32 $intArray : generic $Array $int $long : resolve dits Int64 $CharSeqBuilder : trait $tc : control : withParams $this : param $CharSeqBuilder // These are the actual method definitions of this trait. $appendBoolean : block $here : control : returns $CharSeqBuilder $b : param $boolean // By default, delegate to string form $str = choose $b "true" "false" $return = append $this $str = return $here $return : done $appendChar : block $here : control : returns $CharSeqBuilder $c : param $char // By default, delegate to string form $str = toString $c $return = append $this $str = return $here $return : done $appendCharArray : block $here : control : returns $CharSeqBuilder $str : param $charArray // By default, delegate to 3-arg version $offset : value 0 $len : length $str $return = append $this $str $offset $len = return $here $return : done $appendCharArrayIntInt : abstract : returns $CharSeqBuilder // If one form must be implemented, it is this one $str : param $charArray $offset : param $int $len : param $int : done $appendCharSequence : block $here : control : returns $CharSeqBuilder $s : param $CharSequence // By default, delegate to string form $str = toString $s $return = append $this $str = return $here $return : done $appendCharSequenceIntInt : block $here : control : returns $CharSeqBuilder $s : param $CharSequence $start : param $int $end : param $int // By default, delegate to the 1-arg form $sub = subSequence $s $start $end $return = append $this $sub = return $here $return : done $appendDouble : block $here : control : returns $CharSeqBuilder $d : param $double // By default, delegate to string form $str = toString $d $return = append $this $str = return $here $return : done $appendFloat : block $here : control : returns $CharSeqBuilder $f : param $float // By default, delegate to string form $str = toString $f $return = append $this $str = return $here $return : done $appendInt : block $here : control : returns $CharSeqBuilder $i : param $int // By default, delegate to string form $str = toString $i $return = append $this $str = return $here $return : done $appendLong : block $here : control : returns $CharSeqBuilder $lng : param $long // By default, delegate to string form $str = toString $lng $return = append $this $str = return $here $return : done $appendObject : block $here : control : returns $CharSeqBuilder $obj : param $Object // By default, delegate to string form $str = toString $obj $return = append $this $str = return $here $return : done $appendString : block $here : control : returns $CharSeqBuilder $str : param $String // By default, delegate to char array form $ca = toCharArray $str $return = append $this $ca = return $here $return : done $appendStringBuffer : block $here : control : returns $CharSeqBuilder $sb : param $StringBuffer // By default, delegate to string form $str = toString $sb $return = append $this $str = return $here $return : done $appendCodePointInt : block $here : control : returns $CharSeqBuilder $codePoint : param $int // By default, delegate to char array form $ca = toChars $Character $codePoint $return = append $this $ca = return $here $return : done $capacity : abstract : returns $int : done $charAtInt : abstract : returns $char $index : param $int : done $codePointAtInt : abstract : returns $int $index : param $int : done $codePointBeforeInt : abstract : returns $int $index : param $int : done $codePointCountIntInt : abstract : returns $int $beginIndex : param $int $endIndex : param $int : done $deleteIntInt : abstract : returns $CharSeqBuilder $start : param $int $end : param $int : done $deleteCharAtInt : abstract : returns $CharSeqBuilder $index : param $int : done $ensureCapacityInt : abstract $minimumCapacity : param $int : done $getCharsIntIntCharArrayInt : abstract $srcBegin : param $int $srcEnd : param $int $dst : param $charArray $dstBegin : param $int : done $indexOfString : block $here : control : returns $int $str : param $String // By default, delegate to start-location form $return = indexOf $this $str 0 = return $here $return : done $indexOfStringInt : abstract : returns $int $str : param $String $fromIndex : param $int : done $insertIntBoolean : block $here : control : returns $CharSeqBuilder $offset : param $int $b : param $boolean // By default, delegate to string form $str = toString $b $return = insert $this $offset $str = return $here $return : done $insertIntChar : block $here : control : returns $CharSeqBuilder $offset : param $int $c : param $char // By default, delegate to string form $str = toString $c $return = insert $this $offset $str = return $here $return : done $insertIntCharArray : block $here : control : returns $CharSeqBuilder $offset : param $int $str : param $charArray // By default, delegate to 4-arg version $off : value 0 $len : length $str $return = insert $this $offset $str $off $len = return $here $return : done $insertIntCharArrayIntInt : abstract : returns $CharSeqBuilder $index : param $int $str : param $charArray $offset : param $int $len : param $int // This form should be implemented directly : done $insertIntCharSequence : block $here : control : returns $CharSeqBuilder $dstOffset : param $int $s : param $CharSequence // By default, delegate to string form $str = toString $s $return = insert $this $offset $str = return $here $return : done $insertIntCharSequenceIntInt : block $here : control : returns $CharSeqBuilder $dstOffset : param $int $s : param $CharSequence $start : param $int $end : param $int // By default, delegate to the 1-arg form $sub = subSequence $s $start $end $return = insert $this $dstOffset $sub = return $here $return : done $insertIntDouble : block $here : control : returns $CharSeqBuilder $offset : param $int $d : param $double // By default, delegate to string form $str = toString $d $return = insert $this $offset $str = return $here $return : done $insertIntFloat : block $here : control : returns $CharSeqBuilder $offset : param $int $f : param $float // By default, delegate to string form $str = toString $f $return = insert $this $offset $str = return $here $return : done $insertIntInt : block $here : control : returns $CharSeqBuilder $offset : param $int $i : param $int // By default, delegate to string form $str = toString $i $return = insert $this $offset $str = return $here $return : done $insertIntLong : block $here : control : returns $CharSeqBuilder $offset : param $int $l : param $long // By default, delegate to string form $str = toString $l $return = insert $this $offset $str = return $here $return : done $insertIntObject : block $here : control : returns $CharSeqBuilder $offset : param $int $obj : param $Object // By default, delegate to string form $str = toString $obj $return = insert $this $offset $str = return $here $return : done $insertIntString : block $here : control : returns $CharSeqBuilder $offset : param $int $str : param $String // By default, delegate to char array form $ca = toCharArray $str $return = insert $this $offset $ca = return $here $return : done $lastIndexOfString : block $here : control : returns $int $str : param $String // By default, delegate to start-location form $return = lastIndexOf $this $str 0 = return $here $return : done $lastIndexOfStringInt : abstract : returns $int $str : param $String $fromIndex : param $int : done $length : abstract : returns $int : done $offsetByCodePointsIntInt : abstract : returns $int $index : param $int $codePointOffset : param $int : done $replaceIntIntString : abstract : returns $CharSeqBuilder $start : param $int $end : param $int $str : param $String : done $reverse : block : returns $CharSeqBuilder : done $setCharAtIntChar : abstract $index : param $int $ch : param $char : done $setLengthInt : abstract $newLength : param $int : done $subSequenceIntInt : abstract : returns $CharSequence $start : param $int $end : param $int : done $substringInt : abstract : returns $String $start : param $int : done $substringIntInt : abstract : returns $String $start : param $int $end : param $int : done $toString : abstract : returns $String : done $trimToSize : abstract : done // This is where the user-facing part of the interface is defined; each // item maps a name to an implementation. Registers can point to either // concrete or abstract methods. While register names must differ from one // another within the scope, the names of multiple methods can be the same // as long as their signatures differ (i.e. methods are overloadable). // Note: These definitions must be within the withParams directive // since it scopes all of the method reference registers. = define $tc append $appendBoolean = define $tc append $appendChar = define $tc append $appendCharArray = define $tc append $appendCharArrayIntInt = define $tc append $appendCharSequence = define $tc append $appendCharSequenceIntInt = define $tc append $appendDouble = define $tc append $appendFloat = define $tc append $appendInt = define $tc append $appendLong = define $tc append $appendObject = define $tc append $appendString = define $tc append $appendStringBuffer = define $tc appendCodePoint $appendCodePointInt = define $tc capacity $capacity = define $tc charAt $charAtInt = define $tc codePointAt $codePointAtInt = define $tc codePointBefore $codePointBeforeInt = define $tc codePointCount $codePointCountIntInt = define $tc delete $deleteIntInt = define $tc deleteCharAt $deleteCharAtInt = define $tc ensureCapacity $ensureCapacityInt = define $tc getChars $getCharsIntIntCharArrayInt = define $tc indexOf $indexOfString = define $tc indexOf $indexOfStringInt = define $tc insert $insertIntBoolean = define $tc insert $insertIntChar = define $tc insert $insertIntCharArray = define $tc insert $insertIntCharArrayIntInt = define $tc insert $insertIntCharSequence = define $tc insert $insertIntCharSequenceIntInt = define $tc insert $insertIntDouble = define $tc insert $insertIntFloat = define $tc insert $insertIntInt = define $tc insert $insertIntLong = define $tc insert $insertIntObject = define $tc insert $insertIntString = define $tc lastIndexOf $lastIndexOfString = define $tc lastIndexOf $lastIndexOfStringInt = define $tc length $length = define $tc offsetByCodePoints $offsetByCodePointsIntInt = define $tc replace $replaceIntIntString = define $tc reverse $reverse = define $tc setCharAt $setCharAtIntChar = define $tc setLength $setLengthInt = define $tc subSequence $subSequenceIntInt = define $tc substring $substringInt = define $tc substring $substringIntInt = define $tc toString $toString = define $tc trimToSize $trimToSize : done : done // This bit officially names the trait javaesque.lang.CharSeqBuilder. $j : resolve javaesque lang = define $j CharSeqBuilder $CharSeqBuilder
Flow control interface
while
A piece of code modeled like this in C:
while(i < 3) { if(a()) continue; else if(b()) break; else ++i; }
might be modeled thus in honcho:
: run $whileLoop : control // while(i < 3) $i = get $im $cond = < $i 3 = returnIfNot $whileLoop $cond // if(a()) continue $a = a $this = restartIf $whileLoop $a // if(b()) break $b = b $this = returnIf $whileLoop $b // else ++i $i1 = + $i 1 = set $im $i1 // run doesn't loop by default = restart $whileLoop : done
Complex for
We'll now model a variation on the Perl C-style for, but with five jump destinations:
- restart: jump to before the initializer
- retest: jump to before the condition of this iteration
- redo: jump to just after the condition
- next: jump to just before the iterator
- last: jump past the end of the block.
A piece of code modeled like this:
for($i = 0; $i < 3; ++i) { # body }
can be modeled surprisingly easily in honcho:
: run $restartTarget : control // $i = 0 $im : mutable = set $im 0 : run $retestTarget : control // $i < 3 $i = get $im $cond = < $i 3 = returnIfNot $restartTarget $cond : run $redoTarget : control // Here's the body. // How to implement the five keywords: // restart: restart $restartTarget // retest: restart $retestTarget // redo: restart $redoTarget // next: return $redoTarget // last: return $restartTarget (or $retestTarget) : done // ++i $i1 = add $i 1 = set $i $i1 = restart $retestTarget : done : done
Formats
Formats are simple aggregates that can be decomposed into non-aggregates during compilation. They also provide a straightforward mechanism for implementing default parameters in terms of an ordered structure.
A format block contains any number of required and optional directives optionally followed by a nested format (which has its own special meaning).
For example, consider this set of multiple Scala parameter lists:
(a : Int, b : Int)(c : Int = a + b, d : Int)(e : Int = a * c * d)
As with Scala, a default in a parameter list can reference parameters in previous lists, but not in its own.
The above example would be represented thus:
$ExampleFormat : format $a : required $Int $b : required $Int : format $c : optional $Int $r = + $a $b : return $r : done $d : required $Int : format $e : optional $Int $r0 = * $a $c $r = * $r0 $d : return $r : done : done : done
The result is a compile-time function-like object to be called with only parameters for the positions prior to the nested format block, if any. If there is such a block, the result of the call will be a similar object for the nested block. If not, the result is a packed sequence.
The result differs from a normal function in that it may accept the compile-time-only value $SKIP in place of any optional parameter; the compiler will automatically insert the evaluation of the parameter's optional block. Since $SKIP is compile-time only, there is no possibility of passing it to a typical function, assigning it to a variable, and so forth. Importantly, $SKIP is distinct from runtime none/null values, so a position may allow storage of said values without clashing.
The compiler should fill the right with $SKIP if fewer parameters are provided than specified. Passing $SKIP for a required parameter is an error.
// Fully specified $ea_a = apply $ExampleFormat 0 1 $ea_b = apply $ea_a 2 3 $ea = apply $ea_b 4 $ea0 $ea1 $ea2 $ea3 $ea4 = unpack $ea // (0,1,2,3,4) // Required params only $eb_a = apply $ExampleFormat 0 1 $eb_b = apply $eb_a $SKIP 3 $eb = apply $eb_b $SKIP // $SKIP optional here due to right fill $eb0 $eb1 $eb2 $eb3 $eb4 = unpack $eb // (0,1,1,3,0)
Intermediate results are intentionally opaque. They are also functionally pure; the same prefix can be extended multiple times.
$ec_a = apply $ExampleFormat 4 5 $ec_b = apply $ec_a $SKIP 6 // Using $ec_b more than once $ec1 = apply $ec_b 7 $ec2 = apply $ec_b 13 $ec3 = apply $ec_b $SKIP
A special syntax is provided for using such a structure as a parameter list.
$fn : block // Automatically unpacks $a $b $c $d $e : params $ExampleFormat // Probably an alias for something like // $_packed : param $ExampleFormat // $a $b $c $d $e = unpack $_packed // ... : done
This is only syntactic sugar, though, and the implementation is exactly as if the parameter types had been declared inline. Because of this, no new object necessarily needs to be created to pass parameters as long as intermediate result objects are optimized away.
Only one method per name (on the dits tree) can have default parameters. Formats without optional positions don't count against this total. If, as a language feature, a method should need multiple forms with defaults, those should be given discriminator suffixes (such as foo:withSomeDefaults:0, foo:withSomeDefaults:1, ... for foo()). The language's compiler can then contain logic to point to the correct morph of the method.
A defaults directive is available to retrieve a partially applied functionassociated with the given method and object. The result is virtually identical to the result of the format directive (including that it accepts $SKIP), except that executing the last step results in the return value of the method call rather than the completed structure.
// Get defaults handle for $fn.apply $fnDefaults : defaults apply $fn $fnr_a = apply $fnDefaults 10 11 $fnr_b = apply $fnr_a $SKIP 13 $r = apply $fnr_b $SKIP // Incidentally, the above should translate to something like this (but more // opaque): $fnm_a = apply $ExampleFormat 10 11 $fnm_b = apply $fnm_a $SKIP 13 $fnm_c = apply $fnm_b $SKIP $a $b $c $d $e = unpack $fnm_c $r = apply $fn $a $b $c $d $e
Nominal implementation of the example format would be similar to the following, where it is assumed that an optional value is true, VALUE for a present value or false, (undefined) for a skip. Compilers can/should make optimizations based on the fact that whether a parameter is a skip is always known at compile time.
Notes:
- A block cannot be the parameter to a params directive and can't be returned from a defaults directive; a format can.
- If $b in the following were optional instead of required, $a would be in scope and available to $b's definition. This is not the case in a format, where $a would have to be above the level of $b to be available. This differs from the normal scoping behavior.
$ExampleFormat : block $a : param $Int $b : param $Int $r : block $c_exists : param $Boolean $c_param : param $Int // ignored if !$c_exists $d : param $Int $c : run : returnIf $c_exists $c_param $r = + $a $b : return $r : done $r : block $e_exists : param $Boolean $e_param : param $Int // ignored if !$e_exists $e : run : returnIf $e_exists $e_param $r0 = * $a $c $r = * $r0 $d : return $r : done $r : pack $a $b $c $d $e : return $r : done : return $r : done : return $r : done