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?
Note that I have double posted this here. I hope this is not perceived as rude, but since the community is small, I am not sure whether people usually hang around here or on StackOverflow (or both).
I am slightly confused for a few reasons. The first one is that I was under the impression that type classes in Nim worked like the analogs in - say - Haskell, Scala or Rust. In all of these languages, type classes are in fact a mechanism that is strictly more general than interfaces. Am I misunderstanding something?
More concretely, I understand that I can define type constraints with type classes. But how should I make use of these constraints? I assumed that I could use type classes in function signatures, but if this fails I am not sure where to use them.
I could use inheritance in place of interfaces, but I am wary of doing so, for I'd rather not create a hierarchy of types. Type classes and interfaces are more flexible in defining relations among types. Also, using inheritance I think I would get dynamic dispatch, while I would prefer to have a form of static polymorphism.
I am not sure what you mean by using closures for interfaces. I assume that this would be about defining object-like structures by bundling a few closures over a set of shared local variables, as it is customary in Javascript. I do not understand how this mechanism would replace interfaces, though.
I hope my questions are not too trivial - I am just starting with Nim and I am looking for directions
type
Generator = generic x
next(var x)
Minimizable = generic x
min(x)
ConstantGenerator = object
x: int
proc next(gen: var ConstantGenerator): int = 0
proc min(gen: ConstantGenerator): int = 0
var g = ConstantGenerator(x: 37)
echo (g is Generator)
echo (g is Minimizable)
The only difference is that next takes a var argument, while min does not. This makes g an instance of Generator but not of Minimizable.
I think this is quite surprising, but it may be very well the intended behaviour
type
Generator = generic var x
next(x)
Yet one trivial example:
type
Multipliable = generic x
x is SomeOrdinal
x * x is x.type
proc square(a: Multipliable): Multipliable = a * a
let x = 5
echo x.square # -> 25
# echo "5".square #doesn't compile - string is not Multipliable
Unfortunately generics for now are quite broken, they seem to work for most simple cases now. They worked better earlier and I hope will be fixed, it's very powerful feature.
What about interfaces - they can't be fully implemented without language support (routines invocation arguments type checking needed, for arguments of interface-type). Another very desirable feature.
They worked better earlier and I hope will be fixed, it's very powerful feature.
You don't happen to have some concrete commits that made it worse, do you?
You are right, I inverted the wording: in fact it was: g an instance of Minimizable but not of Generator.
Your solution works well for me: in fact I did not know it was even syntactically valid - Nim syntax is definitely complex.
About interfaces: it seems to me that type classes are in fact more general than interfaces (they certainly are in other languages). The only downside (debatable) is that in Nim they are implicit, so you don't explicitly declare that a type is instance of a type class. But your second example seems to me to work exactly like an interface would
But your second example seems to me to work exactly like an interface would
No, with interfaces you explicitly declare some types as supporting some interface and it is useful when you need that some make some types "distinct", incompatible with each other, although they both support some signature. I.e. every type that complies with Multipliable generic definition is considered as a Multipliable, and with interfaces you would have to explicitly mark types as Multipliable, like say:
type
Multipliable = interface
# some definition
MyInt = int implements Multipliable
echo MyInt(5).square # works
echo 5.square # compile-time error: int is not Multipliable
Araq:
I didn't complain, it's kinda wish...
Ok, I see what you mean: it is about being explicit in implementing an interface.
In fact, in all other languages I know, also typeclasses (where existent) are opt-in and not implicit
No, with interfaces you explicitly declare some types as supporting some interface and it is useful when you need that some make some types "distinct", incompatible with each other, although they both support some signature.
Well, what Go calls interfaces is the opposite of this. I think these terms are pretty loose.
I can also imagine that we could add a way of declaring that a type behaves according to a generic, which would 1: help you verify that your type correctly implements the generic, and 2: makes it clear to others what you intended. But wouldn't 3: Prevent a procedure from accepting a type which did not intend to implement the generic, but just happened to by chance.
I'm not sure I see the big point of 3, though I would like to see 1 and 2. It should already be possible, but it would have to be made into a idiom or macro.
andrea: I would've described your original confusion as trying to construct a type from a type class, while type classes are actually used to describe or constrain a type. It's impossible to go the other way.
It is very understandable to be confused though, because the "generic" keyword is quite misleading. It should probably have been "typeclass", as a generic is actually just a type with a parameter.
http://nim-lang.org/manual.html#generics
That is, I believe this should be possible, if what you wanted to do was to be able to construct a type that contained another type which just had to adhere to the BoundedGenerator typeclass.
type Uniform*[T: BoundedGenerator] = object
gen: T
min_p: float
max_p: float
I'm not sure I see the big point of 3
The point here is that you cannot describe all the needed constraints formally in the interface declaration, so you just declare that this concrete type supports (implements) all that it is meant to be supported by that interface. Points 1 and 2 are auxiliary for it. Generally you can have an interface that doesn't declare any signatures (then you can declare any type as supporting this interface). Important point is that a type can implement any number of interfaces. I don't know what's called interfaces in Go, this concept of interfaces is used in Delphi, Java and some other languages.
UPD
Here http://en.wikipedia.org/wiki/Interface_(object-oriented_programming) both concepts are mentioned, and it seems Go's interfaces is something like Nim's generics, and interfaces in Delphi/Java sense are also in Ada, C#, D and Objective C (there they are called protocls).
@skyfex Thank you, I think this is exactly what I wanted.
At the cost of being pedantic, I will also try to clear some possible misunderstanding. All of the following constructions describe constraints that a type can satisfy, in the form of function signatures:
Scala does not have typeclasses, but has traits (which cover, among other things, interfaces) and implicit conversions (much like Nim converters). Haskell-like typeclasses can be simulated by defining an interface. To make a class C a member of a typeclass T after C has been created (without implementing T) one can implicitly convert C to a wrapper class that implements T.
It seems to me (but really, I am just a beginner with Nim) that typeclasses in Nim are like in Haskell, but - like Go interfaces - they are implemented implicitly. So in a sense
Nim typeclasses : Haskell typeclasses = Go interfaces : Java interfaces.
This may be not precise, because - unlike the other languages mentioned above - it seems to me that Nim allows to express more general constraints that the existence of functions with the given signatures.
I hope the above is precise enough, feel free to correct me!
Nim allows to express more general constraints that the existence of functions
indeed
interfaces in Go; but types implement interfaces implicitly, just by sake of having the right methods
Rather methods with the same signatures, two types can have methods of the same name that are not related somehow, i.e. are semantically different. As opposed to it, with explicit declaration of what interfaces a type supports in Delphi/Java/C#/Objective-C one states the purpose of methods of this type, so if two types implement the same interface then their methods (from this interface signature) are claimed to perform the same task, just being differently implemented. One can have interfaces with empty signature (no methods declared) - just to mark some types as related to something.