The dollar sign ($) in Jai indicates a type variable. 1
Type variables allow for defining a generic form of source code that can work for multiple types. Similar to template metaprogramming in C++, but simpler and cleaner and without any header files.
// from https://youtu.be/BwqeFrlSpuI?t=218
print_number::(number:$T){// pass number as both int and float,
// one of them will work out..
printf("print_number: %d %f\n",number,number);}n1:int=1;n2:u8=2;n3:s64=3;n4:float32=4;n5:float64=5;print_number(n1);// 1 0.000000
print_number(n2);// 2 0.000000
print_number(n3);// 3 0.000000
print_number(n4);// 0 4.000000
print_number(n5);// 0 5.000000
Authoritative parameters
For procedures with multiple parameters of the same type variable, use only one dollar sign to identify which parameter is authoritative over the error message. 2
add::(p:$T,q:T)->T{x:=p+q;// does not handle overflow
returnx;}n1:u8=254;n2:float32=7.63;n3:u8=1;add(n1,n2);// type mismatch error will state second parameter must match first
add(n1,n3);// ok, types match
Vector2::struct{x:float=2.0;y:float=2.1;}Vector3::struct{z:float=3.2;y:float=3.1;x:float=3.0;}using_test::(usinga:$T){printf("a.x is %f\n",x);printf("a.y is %f\n",y);}v2:Vector2;v3:Vector3;// works with values or pointers
using_test(v2);// a.x is 2.0; a.y is 2.1
using_test(^v2);using_test(v3);// a.x is 3.0; a.y is 3.1
using_test(^v3);
Baking polymorphs
See #bake to precompile a polymorphic procedure for a specific type.
baked3:=#bakeusing_test(T=Vector3);
Modifying polymorph types
See #modify to inject one or more procedures for inspecting and reacting to type variables being passed to a polymorphic procedure.
// from: https://youtu.be/7Fsy2WaxLOY?t=2456
concat::(s:string,t:string)->string{returncase(mprintf("%s%s",s,t),string);}length::(usingv:$T)->float#body_text (T: ^Type_Info) -> string {
ifT.type!=Type_Info_Tag.STRUCT{printf("Error: 'length' wants a Vector type, but you gave it something that wasn't a struct!\n");returnnull;}// this leaks; in real code, you would use a string builder here.
squares:="x*x";ifhas_field(T,"y")squares=concat(squares," + y*y");ifhas_field(T,"z")squares=concat(squares," + z*z");ifhas_field(T,"w")squares=concat(squares," + w*w");s:=mprintf("return sqrtf(%s);",squares);printf("** Returning generated body text: %s\n",s);returncast(s,string);// dumb cast!
}Vector2::struct{x:float=1;y:float=4;}Vector3::struct{x:float=1;y:float=4;z:float=9;}Vector4::struct{x:float=1;y:float=4;z:float=9;w:float=16;}v2:Vector2;v3:Vector3;v4:Vector4;printf("Length of v2 is: %f\n",length(v2));printf("Length of v3 is: %f\n",length(v3));printf("Length of v4 is: %f\n",length(v4));
length_body_text.jai
Baking values
Use #bake_values to precompile a regular procedure with specific parameter values:
// from: https://youtu.be/7Fsy2WaxLOY?t=3540
map_test::(){/*
An experiment in programming in a more Lisp-y style.
*/N::5;a:[N]int;for0..N-1a[it]=it+1;foraprintf("a[%d] = %d\n",it_index,it);// map: apply f to each element of array, returning the resulting array.
map::(array:[]$T,f:(T)->$R)->[]R{result:[]R;result.data=malloc(array.count*size_of(R));result.count=array.count;forarrayresult[it_index]=f(it);returnresult;}square::(n:int)->int{returnn*n;}b:=map(a,square);forbprintf("b[%d] = %d\n",it_index,it);free(b.data);}
map_test.jai
what the dollar sign indicates is that this is a type variable which means we don’t know what type it is at declaration time. the type of this variable will be determined when the function is called. “Polymorphic Procedures, part 1” YouTube, uploaded by Jonathan Blow, Apr 1, 2015, https://youtu.be/BwqeFrlSpuI?t=228⮌
the reason that we only use the dollar sign once is that the dollar sign tells us which argument is authoritative over what the type should be.. “Polymorphic Procedures, part 1” YouTube, uploaded by Jonathan Blow, Apr 1, 2015, https://youtu.be/BwqeFrlSpuI?t=360⮌
here we’ve got a function called using_test, and we’re using the first parameter. Remember that using let’s us refer to these components; it’s like a this pointer. “Polymorphic Procedures, part 1” YouTube, uploaded by Jonathan Blow, Apr 1, 2015, https://youtu.be/BwqeFrlSpuI?t=1624⮌
In many programming languages, map is the name of a higher-order function that applies a given function to each element of a functor, e.g. a list, returning a list of results in the same order. Wikipedia contributors. “Map (higher-order function).” Wikipedia, The Free Encyclopedia, 11 Mar. 2019. Web. 31 Jul. 2019., https://en.wikipedia.org/w/index.php?title=Map_(higher-order_function)&oldid=887181717⮌