Nim - Create sequence of objects which implement a method

Viewed 2081

I want to program a game and would like to use a component pattern for multiple entities.

In a language with interfaces / type-classes / multiple inheritance there would be no problem.

I want some entities to be updateable but not renderable and some shall be both.


Haskell:

class Updateable a where
    update :: Float -> a -> a

class Renderable a where
    render :: a -> Picture

class InputHandler a where
    handleInput :: Event -> a -> a

I can create a list of things that can be updated.

updateAll :: Updateable a => Float -> [a] -> [a]
updateAll delta objs = map (update delta) objs

In Java/D/... this could be implemented via Interfaces

interface Updateable {
    void update(float delta);
}

// somewhere in a method
List<Updateable> objs = ...;
for (Updateable o : objs) {
    o.update(delta);
}

Now I am wondering how this can be implemented in nim with multimethods.

Can the existence of a fitting multimethod be expressed in a type?

var objs: seq[???] = @[]



Edit: Added more code and fixed incorrect Haskell example

3 Answers

Swift has the same problem and there they use Type Erasure, which is the same as proposed in the previous comments but a bit more strutured. The general pattern in Nim is like this:

#-------------------------------------------------------------
# types
#-------------------------------------------------------------
type C = concept type C
    proc name(x: C, msg: string): string

type AnyC = object
    name: proc(msg: string): string # doesn't contain C

type A = object
type B = object

#-------------------------------------------------------------
# procs
#-------------------------------------------------------------
proc name(x: A, msg: string): string = "A" & msg
proc name(x: B, msg: string): string = "B" & msg
proc name(x: AnyC, msg: string): string = x.name(msg) # AnyC implements C

proc to_any(x: A): AnyC = AnyC(
    name: proc (msg: string): string = name(x, msg) # x captured by proc
)

proc to_any(x: B): AnyC = AnyC(
    name: proc (msg: string): string = name(x, msg) # x captured by proc
)

# actually use C
proc print_name(x: C, msg: string) = echo x.name(msg)

#-------------------------------------------------------------
# main
#-------------------------------------------------------------

let a = A()
let b = B()

let cs = [a.to_any(), b.to_any()] # the main goal of most erasure cases

for c in cs:
    c.print_name(" erased") # e.g. "A erased"

In this example AnyC implements C, A and B also implement C but more importantly can be converted to AnyC. The Any* types usually contain closures to effectively erase the type and also implement the concept itself by trivial forwarding the arguments.

I wish there was a macro or something that would implement Any* and to_any automatically.

Related