Parametric polymorphism, commonly referred to as “generics”, allow the user to create a procedure or data that can be written generically so it can handle values in the same manner.
Let’s do a little exercise trying to implement filter
and map
for slices. These are already implemented in the core at slice.filter and slice.mapper.
The following code is just an example of parametric polymorphism. A better implementation is available with slice.filter
and slice.mapper
.
The idea is to have a type definition with $
that can be used instead of a specific type like string
. Normally is defined as $T
, but can be named anything like $MyGenericType
. Once is defined it can be used for any other param or declaration in the procedure scope. You can reuse a type in multiple places as a way of saying that they’re the same.
filter
Filters the enumerable
, i.e. returns only those elements for which proc
returns a truthy value.
filter :: proc(enumerable: []$T, callback: proc(element: T) -> bool) -> [dynamic]T {
results : [dynamic]T
for item in enumerable {
if (callback(item)) {
append(&results, item)
}
}
return results
}
Usage
string slice
items := []string{"one", "two"}
filtered := filter(items, proc (element: string) -> bool {
return element == "two"
})
// ["two"]
fmt.println(filtered)
int slice
filtered_int := filter([]int{1, 2, 3}, proc (element: int) -> bool {
return element == 2
})
// [2]
fmt.println(filtered)
map (mapper)
Returns a slice where each element is the result of invoking proc
on each corresponding element of enumerable
.
mapper :: proc(enumerable: []$T, callback: proc(element: T) -> T) -> [dynamic]T {
results : [dynamic]T
for item in enumerable {
append(&results, callback(item))
}
return results
}
Usage
string slice
items := []string{"one", "two"}
mapped := mapper(items, proc(element: string) -> string {
if element == "one" {
return "three"
}
return "four"
})
// ["three", "four"]
fmt.println(mapped)
int slice
items := []int{1, 2, 3}
mapped_int := mapper(items, proc(element: int) -> int {
if element == 1 {
return 2
}
return 4
})
// [2, 4, 4]
fmt.println(mapped_int)
Final Thoughts
-
It is OK to make stuff just for the sake of it, to explore how stuff works. Just be careful not to overcomplicate things.
-
Notice how we can pass
proc
as parameters. It’s the full declaration, but without its implementation.
callback: proc(element: T) -> bool
Thanks to @Barinzaya, @Tetralux and @Jesse for the guidance and corrections.