Instance arguments
Parametric polymorphism (generics) can help you create functions that take or return any type. But sometimes this is too broad and you want functions that only work for a set of types. For this you need ad hoc polymorphism.
Imagine you need a function + that sums two numbers. This function should work for numeric types only. There's no way to implement such a function using generics alone. Instance arguments to the rescue! Instance arguments allow you to create something similiar to type classes and their instances.
Begin code:
module instanceargs
// define your type class
pub
type Plus a = Plus { plus : a -> a -> a }
// the actual function
(+) : {{ Plus a }} -> a -> a -> a
(+) {{Plus {plus}}} x y = plus x y
// instances can be created in any module
// and will always be searched if they are in the current context
pub instance
plusInt : Plus Int
plusInt = Plus { plus: \x y -> Java.sumInt(x, y) }
// using it
theSum = 3 + 4
End code.
Any type can be used as an instance argument as long as it's a named type. You can use the double bracket syntax to annotate a function parameter as an instance argument. These arguments can be explicitily passed to a function using double bracket syntax.
Begin code:
module instanceargs
// creating a function that take an instance argument
showAndPrint : {{ Show a }} -> a -> Unit
showAndPrint {{Show showFun}} x = println (showFun x)
pub
main : Array String -> Unit
main _ =
// the compiler will search for the instance
showAndPrint 4
// explicitily passing the argument
showAndPrint {{showChar}} 'a'
End code.
The Novah compiler can automatically infer the type of an instance argument if it's unambiguous.
Begin code:
module instanceargs
foo : Unit -> Unit
foo () =
// doesn't compile: cannot find Plus instance for x, y
let sum2 x y = x + y
// automatically infer the instance type
let sum {{_}} x y = x + y
println (sum 5.6 9.0)
End code.