Type classes in Nim
Asked Answered
S

1

5

I am trying to make a simple use of typeclasses in Nim. Please, keep in mind that I only have been using Nim since this morning, so I may have been doing something stupid.

Anyway, I would like to define a pseudorandom generator that produces a stream of values of type T. Sometimes T is numeric, hence it makes sense to know something about the minimum and maximum values attainable - say to rescale the values. Here are my types

type
  Generator*[T] = generic x
    next(var x) is T

  BoundedGenerator*[T] = generic x
    x is Generator[T]
    min(x) is T
    max(x) is T

I also have such an instance, say LinearCongruentialGenerator.

Say I want to use this to define Uniform generator that produces float values in an interval. I have tried

type Uniform* = object
  gen: BoundedGenerator[int]
  min_p: float
  max_p: float

proc create*(gen: BoundedGenerator[int], min: float, max: float): Uniform =
  return Uniform(gen: gen, min_p: min, max_p: max)

I omit the obvious definitions of next, min and max.

The above, however, does not compile, due to Error: 'BoundedGenerator' is not a concrete type

If I explicitly put LinearCongruentialGenerator in place of BoundedGenerator[int], everyting compiles, but of course I want to be able to switch more sophisticated generators.

Can anyone help me understand the compiler error?

Scientist answered 14/1, 2015 at 17:55 Comment(0)
S
7

The type classes in Nim are not used to create abstract polymorphic types as it is the case with Haskell's type classes and C++'s interfaces. Instead, they are much more similar to the concepts proposal for C++. They define a set of arbitrary type requirements that can be used as overload-resolution criteria for generic functions.

If you want to work with abstract types, you can either define a type hierarchy with a common base type and use methods (which use multiple dispatch) or you can roll your own vtable-based solution. In the future, the user defined type classes will gain the ability to automatically convert the matched values to a different type (during overload resolution). This will make the vtable approach very easy to use as values of types with compatible interfaces will be convertible to a "fat pointer" carrying the vtable externally to the object (with the benefit that many pointers with different abstract types can be created for the same object). I'll be implementing these mechanisms in the next few months, hopefully before the 1.0 release.

Araq (the primary author of Nim) also has some plans for optimizing a certain type of group of closures bundled together to a cheaper representation, where the closure environment is shared between them and the end result is quite close to the traditional C++-like vtable-carrying object.

Superfluity answered 15/1, 2015 at 11:10 Comment(2)
Both multimethods and vtables use dynamic dispatch at runtime, but it seems in most cases this is not necessary. In fact, usually, most of the type information is known at compile time and the resolution of the correct implementation can be static. In fact, I think that now I understand better this error, and it seems to me that it has not to do with the difference between Nim typeclasses and C++ interfaces. Rather, I think it is just that Nim is not able to determine a correct size for Uniform, given that implementations of BoundedGenerator[int] may have variable sizes.Scientist
Well, I should know what this error means, since I added it to the compiler :) BoundedGenerator[int] is not a specific type that can be instantiated - it's just a verifiable description of the category of types, which behave like BoundedGenerators. You can creates many concrete object types that fit that description.Superfluity

© 2022 - 2024 — McMap. All rights reserved.