I'm trying to get a reference to a method, but with no success. Can anyone provide some insight?
In essence, I'm trying to get the following fake code to be real:
type
TMyObject = object
id: int
method MyMethod(item: TMyObject) =
echo "I'm Alive"
type
PMyMethod = ref method(TMyObject) # how should this be declared?
var m: PMyMethod
m = MethodRef(MyMethod(TMyObject)) # what should this really be?
Sorry it took so long, I was trying to do it quick and dirty on compileonline.com, but apparently the compiler they use is really buggy*. So I resorted to my text editor.
Anyway, methodvars work just like procvars. Label the function you want with the {.procvar.} pragma. Then the type shouldn't be a ref to the MyMethod, but instead a procvar with the same arguments/return as the method.
type
TMyObject = object
id: Int
method MyMethod(self: TMyObject) {.procvar.} =
echo "I'm Alive!"
type
PMyMethod = proc(self: TMyObject)
let
obj: TMyObject = TMyObject(id: 5)
m: PMyMethod = MyMethod
obj.m()
But remember, if you just pass the object into a function, it can still call that objects functions. And methods are really there for when you need dynamic dispatch, not for replacing simple procs.
Also, might I ask why you need this functionality? I understand a few cases you might, like a (weird) generic container, but this seems pretty useless. All you're doing is renaming the method, and you still need the original object. So in 99.9% cases, this is the wrong way to go about it. Just a warning.
Thanks @NewGuy. Your, "Sorry it took so long", made me laugh.
Your code works, of course, but by removing the ref in the PMyMethod type definition it breaks the code where I'm trying to apply this idea. You're right that my code is pretty useless. It was a simplification of a piece of what I was trying to accomplish in another thread.I'm trying to implement cross cutting concerns in Nimrod like would be found in a traditional interface style. I needed the method references to make indirect calls out of an interface and back into their defining class. I've posted the "real" code here if you'd give it a look.
http://forum.nimrod-code.org/t/278
Maybe you have a solution for what I have or possibly a better more ... Nimrodic ... way of approaching this?
I'm really new to Nimrod, with only about 8 hours coding time in now. I spent the last couple weeks looking things over and comparing Nimrod features to my "language wishlist." I don't like the direction Microsoft is headed so I'm looking for something that could be a replacement for C#. I saw a Nimrod post someplace (Reddit or HN maybe) when .9.2 came out and am now looking at Nimrod in more detail. I've "feature list compared" many languages now including: D, Go, Dylan, some Java VM languages Kotlin, Ceylon, Gosu, Scala and some other interesting but non-replacement languages like Dart, Io, Lua, etc. Nimrod holds fairly well feature wise but does have some surprising oddities, like the lack of interfaces. So I picked that as my first "see how it could be handled" item.
Ah, I see. Well, this is one of the few things in Nimrod I've found... not fun. I really, really want proper implicit interfaces ala Go, or at least something like Traits in Rust. Even in C++ you can work around this with virtual = 0 methods, and not be buggered by a lack of multiple inheritance.
Unfortunately, I don't have a work-around for you.
Nimrod holds fairly well feature wise but does have some surprising oddities, like the lack of interfaces.
That's because I dislike interfaces and consider them the most heavily overused misfeature, pretending runtime dispatching to be a clean solution. They also don't play nice with Nimrod's effect system.
However, apart from reflection and interfaces some features are missing because features tend to start in the state unimplemented until some kind soul puts them to implemented.
Newguy: Well, this is one of the few things in Nimrod I've found... not fun.
It's probably a matter of background. To an OCaml or SML programmer, for example, Nimrod's type system would feel perfectly natural (other than the "pollution" with imperative concepts). Combining OO-style subtype polymorphism with type inference can be a somewhat painful experience, so quite a few languages restrict themselves to the limited polymorphism allowed by variant types combined with parametric polymorphism.
Parametric polymorphism in particular can substitute for a number of uses of subtype polymorphism and is often the better choice. Subtype polymorphism is useful when you're dealing with truly heterogeneous data, and it is fairly rare that you have data that is heterogenous in two fundamentally different ways.
That's because I dislike interfaces and consider them the most heavily overused misfeature, pretending runtime dispatching to be a clean solution.
I've no problem with your dislike of interfaces. I'm no language purist and certainly can't be wed to a detail like this with such a short time into Nimrod. So I guess the question then becomes, what's the Nimrod way to abstract away types of behavior without needing to know where the behavior is implemented nor care about how it is implemented?
Will Rubin: So I guess the question then becomes, what's the Nimrod way to abstract away types of behavior without needing to know where the behavior is implemented nor care about how it is implemented?
Most of the time, parametric polymorphism will do what you need. For example, instead of doing:
proc feed(animal: FeedableInterface) = ...
where FeedableInterface is an interface or superclass of all acceptable types, you can do:
proc feed[SomethingFeedable](animal: SomethingFeedable) = ...
where SomethingFeedable is a type parameter that can range over subclasses of FeedableInterface (except that they don't need to be subclasses, just support the necessary procedures/methods).
Note that parametric polymorphism is not a full substitute for subtype polymorphism, and vice versa. Parametric polymorphism relies on knowing the type at compile time, whereas subtype polymorphism allows you to delay that until runtime (early binding vs. late binding). However, parametric polymorphism also allows you to do things that subtype polymorphism alone can struggle with (such as having constraints involving more than one type, or creating instances of the type parameter) and in practice, you can express almost anything using a combination of parametric polymorphism and subtype polymorphism restricted to single inheritance/variants. Abstracting behavior by itself is rarely served well by subtype polymorphism; subtype polymorphism is generally best for dealing with heterogeneous data (abstract syntax trees and the command pattern are good examples).
What Nimrod currently doesn't allow you to do is expressly specify constraints on the signature of the type parameter. That's what the upcoming type class extensions are about. Right now, Nimrod infers a type's signature from whether a procedure is overloaded, or by using a mixin statement where it isn't.
If you do need multiple interfaces, then you can use the adapter pattern; this can be combined with converter functions to avoid unnecessary conversion boilerplate (the converter creates an instance of the target type or of a subtype of the target type).
NB: My own preference would be for there to be multiple inheritance (interface or implementation+interface) and/or structural subtyping. At the same time, there are always tradeoffs (plus the implementation effort, as Araq noted), and it isn't even remotely a crippling problem (plenty of languages, especially in the functional world, do fine without). It does, however, require a slightly different approach to architecting large software systems.
Jehan, you're right on the money. I'm definitely coming from the background of languages that either allow multiple inheritance with virtual = 0 methods or explicit/implicit interfaces. It's a nice feature that was useful, and I would still really love it if Nimrod added something like Traits in Rust where it's not implicit, but instead explicit traits of a type. That way it's statically typed and the programmer has to meaningfully acknowledge and desire the trait to be implemented.
But I can definitely live without, and I've got by just fine without them. A couple inconveniences, but truly nothing bad.
Jehan: Thanks for the answer here and for the interesting direction using the mixins on the other thread. I agree there are many ways to overcome this and was just looking to get one to work so I would know I could remove it from the "no" side of my checklist.
My first try was function pointers with an instance reference in an object . I couldn't get it to work though because I couldn't find the "&" operator, as it were. Now I'm trying Araq's tuple closure technique (almost the same thing ... one more indirection but very clean looking code) but still can't get it to work. That approach with generics is, I believe, very close to what you're describing above. I had that coded earlier today but Type issues are biting me in the behind every time I try something. I've pulled out the generics and still can't get it to work. Still trouble binding a method reference I believe. I'll post what I have momentarily to see if someone can point out what I'm doing wrong.
Here is my code for a non-generic (and non-working) closure style interface. I've marked my two problem spots with ???:. Does anyone spot what I'm doing wrong here and have the right syntax?
-- Thanks
type
TCat = object
name: string
clawSharpness: int
IMoverAndShaker = tuple[
iMove: proc(self: TCat): TCat {.closure.}, # Bug? compiler error if discardable
iShake: proc(self: TCat): TCat {.closure.}]
method SayHello(self: TCat) =
echo "Hello from Cat named: " & self.name
method Move(self: TCat): TCat {.discardable, procvar.} =
echo "Cat '", self.name, "' is Moving"
return self
method Shake(self: TCat): TCat {.discardable, procvar.} =
echo "Cat '", self.name, "' is Shaking"
return self
method getIMoverAndShaker(self: TCat): IMoverAndShaker =
# return cast[IMoverAndShaker]((iMove: Move, iShake: Shake)) # I thought this was correct, but it appears not.
return MAGIC # ???: What do I return here?
# ----------------------------------------------- #
echo "Starting"
var cat = TCat(name: "Barfield", clawSharpness: 5)
cat.SayHello()
cat.Move()
var i: IMoverAndShaker = cat.getIMoverAndShaker()
discard i.iMove() # ???: Why, "Error: type mismatch: got () but expected one of: proc (TCat): TCat{.closure.}"
First of all, I strongly recommend using ref object instead of object. Polymorphism for object types does not work the way you probably think it does (plus, there's a bug for which I still need to file a bug report). Briefly, object is the equivalent of a C# struct (plus limited inheritance capabilities), ref object is the equivalent of a C# class.
Now, on to your problem. The usual trick is to pass the self or this variable of an object-oriented construction in the environment of a closure instead of as an extra parameter, e.g.:
type
TCat = ref object
name: string
clawSharpness: int
IMoverAndShaker = tuple[
iMove: proc(): TCat {.closure.},
iShake: proc(): TCat {.closure.}]
proc SayHello(self: TCat) =
echo "Hello from Cat named: " & self.name
proc Move(self: TCat): TCat {.discardable, procvar.} =
echo "Cat '", self.name, "' is Moving"
return self
proc Shake(self: TCat): TCat {.discardable, procvar.} =
echo "Cat '", self.name, "' is Shaking"
return self
converter AdaptToIMoverAndShaker(self: TCat): IMoverAndShaker =
(iMove: proc(): TCat = Move(self),
iShake: proc(): TCat = Shake(self))
# ----------------------------------------------- #
echo "Starting"
var cat = TCat(name: "Barfield", clawSharpness: 5)
cat.SayHello()
cat.Move()
discard cat.Move()
I note that the closure table approach is pretty expensive in terms of memory. Each closure needs two memory words, one for the procedure reference, one for the environment reference, and then the data for the environment itself.
You can avoid this overhead as follows:
type
TCat = ref object of TObject
name: string
clawSharpness: int
TDog = ref object of TObject
name: string
barkLevel: int
proc Move(self: TCat) =
echo "Cat ", self.name, " is moving."
proc Move(self: TDog) =
echo "Dog ", self.name, " is moving."
proc Shake(self: TCat) =
echo "Cat ", self.name, " is shaking."
proc Shake(self: TDog) =
echo "Dog ", self.name, " is shaking."
type
IMoverAndShaker = ref object {. inheritable .}
MoverAndShaker[T] = ref object of IMoverAndShaker
self: T
method Move(obj: IMoverAndShaker) =
echo "not implemented"
method Shake(obj: IMoverAndShaker) =
echo "not implemented"
method Move[T](obj: MoverAndShaker[T]) =
obj.self.Move()
method Shake[T](obj: MoverAndShaker[T]) =
obj.self.Shake()
converter AsMoverAndShaker[T](obj: T): IMoverAndShaker =
proc NeverExecuted() =
# Hack to ensure that code for Move and Shake is generated
var tmp: MoverAndShaker[T]
tmp.Move; tmp.Shake
MoverAndShaker[T](self: obj)
var cat = TCat(name: "Barfield", clawSharpness: 314)
var dog = TDog(name: "T-Rex", barkLevel: 2718)
var animal: IMoverAndShaker = cat
var critters: seq[IMoverAndShaker] =
@[AsMoverAndShaker(cat), AsMoverAndShaker(dog)]
for critter in critters:
critter.Move
critter.Shake
Note that this implementation has to work around a problem with the Nimrod compiler to actually generate code for Move[T] and Shake[T]. Also, while this implementation has only constant overhead, it requires a heap allocation per call to AsMoverAndShaker; getting rid of the heap allocation requires some non-trivial effort to work around the type system.