Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Nim return custom tuple containing proc

I'm trying to create a proc that returns a custom tuple that contains a single element that is a proc type i.e.

type
  CustomTuple = tuple
    foo: proc(input: int): int

proc createCustomTuple(): CustomTuple =
  (foo: proc(input: int): int = 10)

However, when I compile this I get the following error (I am compiling with Nim Version 1.2.6 on Windows).

Error: type mismatch: got <tuple[foo: proc (input: int): int{.noSideEffect, gcsafe, locks: 0.}]> but expected 'CustomTuple = tuple[foo: proc (input: int): int{.closure.}]'

So the compiler thinks I am returning a regular tuple and not a CustomTuple but I have no idea how to change this to make it work. The documentation for tuples in the Nim manual show custom tuples being constructed in the way that I am doing it and I couldn't find any examples of returning a custom tuple from a proc.

If I change my CustomTuple definition to contain types that aren't procs then it compiles successfully so it appears it has something to do my custom tuple containing a proc that is causing this to fail to compile.

Can anyone explain why the above code is not compiling?

like image 838
Benjamin Gale Avatar asked Aug 28 '20 18:08

Benjamin Gale


2 Answers

I think the reason my original code isn't working is because Nim is unable to convert the proc to a closure automatically when it is added to a tuple. There is some discussion around this in the Nim forums.

As the proc is not converted to a closure the compiler is unable to determine that the tuple being returned is a CustomTuple because the types don't match which explains the error message.

So when packing a proc in a tuple you need to explicitly convert it to a closure. This can be done by explicitly casting the proc like this:

type
  CustomTuple = tuple
    foo: proc(input: int): int

proc createCustomTuple(): CustomTuple =
  (foo: (proc(input:int):int)(proc(input: int): int = 10))

or by adding a {.closure.} pragma like this (which I think is much cleaner).

type
  CustomTuple = tuple
    foo: proc(input: int): int

proc createCustomTuple(): CustomTuple =
  (foo: proc(input: int): int {.closure.} = 10)

If you don't want to do either of these you can assign the proc to the foo property of the implicit result variable as per Salewski's answer.

This also explains why Salewski's original solution of assigning the proc to the foo property of the implicit result variable worked; the compiler is able to automatically convert the proc to a closure in that case.

like image 56
Benjamin Gale Avatar answered Nov 17 '22 04:11

Benjamin Gale


type
  CustomObject = object
    foo: proc(input: int): int

proc createCustomObject(): CustomObject =
  #(foo: proc(input: int): int = 10)
  #result.foo = proc(input: int): int = 10
  CustomObject(foo: proc(input: int): int = input * 2)

var x = createCustomObject()
echo x.foo(2)
like image 1
Salewski Avatar answered Nov 17 '22 06:11

Salewski