Nim: "invalid type ... in this context" error

Viewed 295

I'm trying to use Nim with Elm. Elm has a port system which can send and receive messages, similar to web workers.

I have this code in the main module:

import ./elm
import ./portMessages


type
    State = object
        elmProgram: elm.Program
        portToElm: elm.SubPort[portMessages.Outgoing]


# Error is with this function
proc newState(
    elmProgram: elm.Program,
): State =
    return State(
        elmProgram: elmProgram,
        portToElm: elmProgram.getSubPort("rawSubscribe"),
    )

When I try compiling, I get this error message from the compiler:

Error: invalid type: 'SubPort[SubPort.T]' in this context: 'proc (elmProgram: Program): State' for proc

This is the elm module. The SubPort type allows T to be sent through a port.

type
    Program* {.importjs.} = ref object of JsRoot
    SubPort*[T: JsRoot] {.importjs.} = ref object of JsRoot


# Get a reference to a Sub port (to Elm)
proc getSubPort*[T: JsRoot](
    program: Program,
    name: cstring,
): SubPort[T] {.importjs: "#.ports[#]".}

This is the 'portMessages' module:

type
    OutgoingKind* {.importjs.} = enum
        portErrorPopup = 0,
    
    Outgoing* {.importjs.} = ref object of JsRoot
        case kind*: OutgoingKind
        of portErrorPopup:
            msg*: cstring
2 Answers

the type mismatch is because getSubPort("messages") is still an underspecified generic, as its returntype is SubPort[T], which can't be inferred from its parameters.

meanwhile, you've specified that your State type's member portToElm has the type SubPort[Outgoing]

The answer should be as simple as:

proc newState(elmProgram: Program): State =
    State(
        elmProgram: elmProgram,
        portToElm: getSubPort[Outgoing](elmProgram,"rawSubscribe")
    )

but this still doesn't work because of a compiler bug. The workaround is to use concepts for the generic restriction:

in elm:

type
  Program* {.importjs.} = ref object of JsRoot
  IsJsRoot = concept type t
    t is JsRoot
  SubPort*[T: IsJsRoot] {.importjs.} = ref object of JsRoot

proc getSubPort*[T:IsJsRoot](
    program: Program, name: cstring): SubPort[T]{.importjs: "#.ports[#]".}

(I don't understand why SubPort needs to be generic in the firstplace, especially as it's importjs, but that's beside the point)

Remove : JsRoot from the definition of the SubPort type:

type
    # ...
    SubPort*[T] {.importjs.} = ref object of JsRoot

It's also necessary to specify portMessages.Outgoing when calling getSubPort:

return State(
    # ...
    portToElm: elmProgram.getSubPort[: portMessages.Outgoing]("rawSubscribe"),
)
Related