This blog post will deal with an experimental approach to higher order functions at compile time, at the type level. It’s motivated in part by my previous post on ternary
Nat, Counting to Infinity at Compile time.
You needn’t understand the details of
TNat (or its binary cousin
BNat), just know that they are different implementations of the concept behind
shapeless.Nat that do not use the usual
A typeclass which takes arguments (type parameters) and maybe returns a type (an abstract type member implemented by the typeclass’s implicits). It will almost certainly follow the aux pattern. …
We saw in a previous blog post of mine an introduction to counting at compile time in scala, using the conventional
Nat is slow. Really, really slow, and there are hard limits on what we can make the scala compiler do, as it currently stands.
For example, here are some rough and ready and entirely-unscientific benchmarks I’ve just run on approximate compile times for the
Sum typeclass we defined last time. The numbers would be completely different on your machine, but probably to the right order of magnitude:
Sum | Approx Time
3+6 | 0s
8+8 | 0s
10+7 | 0s
12+12 | 2s
15+15 | <does not complete>
17+4 | 1s
20+5 | 7s
20+8 | 104s
22+11 | <does not complete>
22+22 | <does not…
Counting at compile time is one of the world’s simple pleasures. We have taken the scala compiler and hacked something in to it to make it do things it really doesn’t look like it should be doing.
The standard approach of ‘counting at compile time’ utilises a type commonly called
Nat. It’s available in libraries such as shapeless. We have types available, that look like this:
type _13 = <something> — this represents the number 13.
<something> is yet to be revealed.
Through type-class trickery and a clever definition of
something we can make the compiler perform magic for us, and turn two types such as
_3 into a third type
_16 by ‘adding’ them together, all at compile time. You can, of course, also make it do any other arithmetic operation you can dream of, if you have the time to implement it. …
It provides a mapping between arbitrarily nested types, provided the compiler can calculate a path between them, all powered with gratitude by
Generic from shapeless.
A quick example:
Target here could be any combination of the atomic source types, in any order and in any nesting. It works in exactly the same way for case classes and hlists, in both source and target position, too.
What else can it do?
We saw in the previous blog post that a particular use-case might be centred around manipulating similar case classes into one another. …
The library alphabet-soup can be found here.
Alphabet-soup is a scala library to provide faster compiler-time type-mixing, to reduce boilerplate and the cognitive overhead of reading and writing canonical code.
If there’s only one way to write a piece of code, why do we have to write it? The compiler should do it for us.
One major difference about alphabet-soup’s approach to the usual type-transformation libraries is that it is entirely type based and not name based. If you map a case class to a case class, it does not care what the
field names are, only the types. …
This blog post will assume terminology from the functional scala library Cats. It is easily applicable to Scalaz, too.
Libraries like Cats and Scalaz give you a verb ‘traverse’, for easier handling of combinations of wrapper types.
It allows you to turn something clunky like this:
That is, it does the
map and the
sequence for you. It applies the function to the collection, and pulls the wrapper type through to the outside.
Of course it’s not limited to
Seq. The ‘collection’ type can be anything that is “traversable” (lists, options, matrices, tuples, trys — it’s a supertype of foldable). …
In a previous blog post I introduced a way to get unboxed arbitrary arity union types in scala. We can write
foo allows an
Int or a
String to be passed to it, and nothing else.
But escaping the union context, ie getting the
Int or the
String out again from our generic
T, wasn’t safe. We had to do this:
The above is not checked by the compiler, either for exhaustivity (if we’d missed
Int it wouldn’t tell us) nor precision (if we included an extra type we didn’t need it wouldn’t tell us).
But it is certainly the easiest way to naively extract our original value again. …
When writing our project, we wanted to pass a static sorting object all the way through our stack from the API (where it was parsed automatically) to Slick, and have the database results be sorted automatically without any extra input from the end-user (ie you, the feature-developer).
(In this post I will assume you are familiar with Slick 3 and will always have the appropriate slick implicits and classes in scope.)
It isn’t obvious how to make slick do this automatic sorting for us. Imagine this table set up:
Just a table to store my dogs in. Now imagine I had a static
Sorting type, and I wanted to sort a dog query by an object of type
Sorting which I’d been supplied by our…
Most strings that you store in a traditional SQL database have a maximum length allowed. This can frequently cause problems, since this maximum length isn’t known by your code.
You might have some checks dotted around, maybe on your API or DB layer, that only sensible length strings get into your system — but you probably don’t have them for every single field. Mutating strings, such as adding two fields together, also invalidates such checks — it will likely just cause a run-time error. One of the nice things about a type checker and functional programming in particular is that it enables and encourages you to remove entire classses of exceptions from your code. …