Search code examples
typescompiler-errorsnim-lang

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


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

Solution

  • 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)