Nim template where early binding is not possible? - nim-lang

There's the build_type template defined in the lib.nim library.
template build_type*[T](_: type[T]): T = T.build()
The object B uses that template to build object A.
And it doesn't work - because while A is visible in the b.nim, it is not visible in main.nim, where B is used.
It works if A imported into main.nim (see commented out import), but that feels wrong as it breaks encapsulation of the B internal details (as the code using B should also import A even if it doesn't use A).
I wonder if there's other way to make it work?
playground
# main.nim ------------------------------------------
import bobject #, aobject
echo B.build()
# bobject.nim ---------------------------------------
import lib, aobject
type B* = tuple[a: A]
proc build*(_: type[B]): B = (a: build_type(A))
# aobject.nim ---------------------------------------
type A* = tuple[v: int]
proc build*(_: type[A]): A = (0,)
# lib.nim -------------------------------------------
template build_type*[T](_: type[T]): T = T.build()
Compilation error:
/main.nim(3, 7) template/generic instantiation of `build` from here
/bobject.nim(5, 44) template/generic instantiation of `build_type` from here
/lib.nim(1, 43) Error: type mismatch: got <type A>
but expected one of:
proc build(_: type[B]): B
first type mismatch at position: 1
required type for _: type B
but expression 'T' is of type: type A
expression: build(T)

I would make it work changing bobject.nim to:
proc initB*(): B =
(a: build_type(A))
And fixing main.nim to:
echo initB()

Related

Macro to generate function implementation by its definition?

Would it be possible to automate writing the implementation for remote function?
If written by hand it would look like code below. Having both function declaration and implementation is important.
proc rcall*[A, B, R](fn: string, a: A, b: B, _: type[R]): R =
echo (fn, a, b)
proc multiply(a, b: int): int
proc multiply(a, b: int): int =
rcall("multiply", a, b, int)
I would like to automate it and write it as
proc rcall*[A, B, R](fn: string, a: A, b: B, _: type[R]): R =
echo (fn, a, b)
proc multiply(a, b: int): int
remotefn multiply
And the remotefn macro should look at function definition and generate its implementation as rcall("multiply", a, b, int), is that possible?
Would it be possible to automate writing the implementation ...
Yes, it would be possible to automate implementation of just about anything using nim macros that take typed argument (and then getTypeImpl)
import std/macros
macro dumpImpl(arg: typed): untyped =
echo arg.getTypeImpl().treeRepr()
proc rcall*[A, B, R](fn: string, a: A, b: B, _: type[R]): R =
echo (fn, a, b)
proc multiply(a, b: int): int
proc multiply(a, b: int): int =
rcall("multiply", a, b, int)
dumpImpl multiply
Shows that multiply type (function signature) has the following structure:
ProcTy
FormalParams
Sym "int"
IdentDefs
Sym "a"
Sym "int"
Empty
IdentDefs
Sym "b"
Sym "int"
Empty
Empty
Although it is important to keep in mind that overloaded procedures cannot be easily resolved based only on name (because, well, there are many implementations). The most obvious choice would be to still use typed argument, but pass some parameter to disambiguate the function call
import std/macros
macro dumpImpl(arg: typed): untyped =
echo arg.treeRepr()
proc overload(a: string) = discard
proc overload(a: int) = discard
dumpImpl overload
# ClosedSymChoice - lists all possible overloads with their respective symbols
# Sym "overload"
# Sym "overload"
dumpImpl overload("123")
#Call
# Sym "overload"
# StrLit "123"
dumpImpl overload(123)
#Call
# Sym "overload"
# IntLit 123
As a small (personal) side note - when you are talking about nim macros the question should mostly be not "if this is even possible?" but rather "what is the most optimal way to do this?". It might require some knowledge of macro tricks, but it is possible to implement almost anything.
EDIT1 (add implementation code example, reply to question in comments):
import std/[macros]
proc rcall*[A, B, R](fn: string, a: A, b: B, _: type[R]): R =
echo (fn, a, b)
macro remotefn(fn: typed) =
let fname = fn.str_val()
# `quote do` generates hygienic identifiers - i.e. all new
# variables/functions introduced by it are unique and not visible in
# global. To fix this you need to explicitly create identifier for your
# procedure using `ident`
let
multId = ident("multiply") # < Function name identifier
# Same goes for variables - nim does have a name-based overloading, so
# you need to make sure function arguments use the same identifiers as
# the original one
aId = ident("a")
bId = ident("b")
result = quote do:
proc `multId`(`aId`, `bId`: int): int =
rcall(`fname`, 1, 1, int)
echo result.toStrLit()
proc multiply(a, b: int): int
remotefn multiply

mypy (typeshed) rejects csv.excel as a possible value for the return of csv.Sniffer.sniff

Using mypy to statically check some of my code and got this problem. Code example:
import csv
d: csv.Dialect = csv.excel
d = csv.Sniffer().sniff("a")
but mypy gives this error in the first assignment to d:
error: Incompatible types in assignment (expression has type "Type[excel]", variable has type "Dialect")
So the natural fix for this is to change the type of the variable.
from typing import Type
import csv
d: Type[csv.Dialect] = csv.excel
d = csv.Sniffer().sniff("a")
But now I get this error on the second assignment to d:
error: Incompatible types in assignment (expression has type "Dialect", variable has type "Type[Dialect]")
But this is odd because csv.excel is a valid return for the sniff function so surely they must have the same type.
Python 3.7.3, mypy 0.701
I think this is a bug in the typeshed: I've raised an issue
According to typeshed, Sniffer.sniff returns a value of type csv.Dialect whereas in fact it returns a value of type Type[csv.Dialect]

python 3.7.0 mypy 0.641 extending pythons' list with UserList?

I am trying to extend pythons’ list with some custom methods, for that I
am creating an class inheriting from UserList.
I am not sure what is the right way and I would like to get mypy play
nicely with UserList.
I consulted cpython UserList docs and searched inside mypy for
UserList but couldn’t find anything.
Using:
mypy 0.641
python 3.7.0
This is a minimal example of what I am trying to achive
from collections import UserList
from typing import List, Optional, Union
class A:
...
class B:
...
class C:
...
TMessage = Union[A, B, C]
class MyList(UserList):
"""Minimal example"""
def __init__(self, data: Optional[List[TMessage]] = None) -> None:
self.data: List[TMessage] = []
if data:
self.data = data[:]
def get_last(self) -> TMessage:
return self.data[-1]
# other methods to be added ...
some_data = [A(), B(), C(), C(), B(), A()]
my_list_a = MyList(some_data)
my_list_b = MyList(some_data)
my_list_b = my_list_a[3:]
Mypy complains as follows
~/tmp ❯❯❯ mypy mypy_userlist.py
mypy_userlist.py:34: error: Argument 1 to "MyList" has incompatible type "List[object]"; expected "Optional[List[Union[A, B, C]]]"
mypy_userlist.py:35: error: Argument 1 to "MyList" has incompatible type "List[object]"; expected "Optional[List[Union[A, B, C]]]"
mypy_userlist.py:37: error: Incompatible types in assignment (expression has type "MutableSequence[Any]", variable has type "MyList")
I could add # type: ignore to the conflicting lines but I would like
to avoid that.
What is the right way to extend the python’s list with custom methods
and get mypy happy?
I'm fairly new at MyPy but I think you have two issues. The first is that lists are mutable, so although your list object some_data satisfies the required structure in your code, there's no reason that an object, not of type A, B or C couldn't be added to it later, meaning at compile tile, Mypy can't ensure that
my_list_a = MyList(some_data)
is a valid assignment. (have a look at the common issues section of the Mypy docs here for more discussion)
You can fix this by explicitly annotating some_data:
some_data : List[TMessage] = [A(), B(), C(), C(), B(), A()]
The second problem will pop up when you fix this, when you try and assign your two lists using slicing. MyPy won't know what your slice function will return and will complain about incompatible types.
To fix this, you can explicitly implement the slice functionality into your class.
def __getitem__(self, slice_indices) -> 'MyList':
return self.data[slice_indices]

what is nim type definition for generic procedure?

I have a strategies expressed as generics in nim:
proc fooStrategy[T](t: T, ...)
proc barStrategy[T](t: T, ...)
I would like to create a lookup table for the strategies by name... so I tried:
type
Strategy*[T] = proc[T](t: T, ...)
let strategies* = toTable[string, Strategy[T]]([
("foo", fooStrategy), ("bar", barStrategy)])
This doesn't work -- the type declaration fails. If I were to get by that I could guess that the table of strategies would also have problems. Is there another way to do this? "T" is supposed to be "some 1D collection type" -- could be sequence, array, vector from blas, etc. I could add concrete strategies to the table for common collections, but I still have the problem with the function pointer, as
type
Strategy* = proc(t: any, ...)
let strategies* = toTable[string, Strategy]([
("foo-seq[int]", fooStrategy[int]), ...])
still has problems. Any suggestions?
There are multiple issues with your code:
Firstly, initTable does not take a list of items for the table. It only takes an initial size. You want to use toTable instead.
Secondly, you must explicitly set a value for the generic parameter T when creating a table, because at runtime, all generic parameters must be bound to a type.
Thirdly, the proc types have to exactly match, including pragmas on the proc. This one's tricky.
Here is a working example:
import tables
type
Strategy*[T] = proc(t: T) {.gcsafe, locks: 0.}
proc fooStrategy[T](t: T) = echo "foo"
proc barStrategy[T](t: T) = echo "bar"
let strategies* = toTable[string, Strategy[int]]([
("foo", fooStrategy[int]), ("bar", barStrategy[int])
])
For this example, I create a table with Strategy[int] values (you cannot have a table with Strategy[T] values as this is not a concrete type). I instantiate both fooStrategy and barStrategy with [int] to match the table type. I added {.gcsafe, locks: 0.} to the type definition. If this is omitted, you will get a compiler error:
test.nim(9, 49) Error: type mismatch: got (Array constructor[0..1, (string, proc (t: int){.gcsafe, locks: 0.})])
but expected one of:
proc (pairs: openarray[(string, Strategy[system.int])]): Table[system.string, Strategy[system.int]]{.gcsafe, locks: 0.}
As you see, the compiler tells you in the first line what it sees and in the third line what it expects. it sees procs with {.gcsafe, locks: 0.} because those pragmas are implicitly assigned to the procs defined above. The pragmas change the type, so to be able to assign those procs to Strategy[T], you have to define the same pragmas to Strategy[T].

What does positional PICK on an object do in Rebol 2, and what's the equivalent Rebol 3?

In Rebol 2:
>> foo: make object! [a: 10 b: 20]
>> foo/a
== 10
>> foo/b
== 20
>> first foo
== [self a b]
>> second foo
== [make object! [
a: 10
b: 20
] 10 20]
>> third foo
== [a: 10 b: 20]
>> fourth foo
** Script Error: fourth expected series argument of type:
series date port tuple event
** Near: fourth foo
So you can pick out of it as if it were a block for values 1, 2, 3. But doing positional selection is right out in Rebol 3:
>> first foo
** Script error: cannot use pick on object! value
** Where: first
** Near: first foo
I gather that this is deprecated now (like picking out of a function to get its parameter list). However, I'm trying to translate some code that says something like:
bar: construct/with (third foo) mumble
(a) What is the point of that code?
(b) How would I translate it to Rebol 3?
This usage of first, second, third, etc for reflection is indeed deprecated (and it's probably quite obvious why).
The general replacement is REFLECT, which takes a FIELD parameter to specify what information is to be extracted.
REFLECT is in turn wrapped by a group of functions (referred to by some as "reflectors") for convenience: SPEC-OF, BODY-OF, WORDS-OF, VALUES-OF, etc. Those are the preferred replacement for reflection using FIRST et al. Luckily, those "reflectors" have been backported to R2 (2.7.7+) as well.
How to translate third foo to Rebol 3?
The counterpart to reflective THIRD on an object is BODY-OF.
What is the point of the construct/with (third a) b idiom?
It allows you to construct a new object by merging A and B (with values from A taking precedence over B).
So you could, for example, use this idiom to create a full "options" object by merging actual user-provided options with an object of defaults.
a) Construct builds the object without evaluating the spec block. This implies that the spec is of some [set-word! any-type!] form (which it would always be if you are using the body of another object). Construct/with uses a second object (mumble) as the prototype.
b) Object operations appear to have changed as follows:
i) first object is replaced by words-of object
ii) second object is replaced by values-of object
iii) third object is replaced by body-of object or to block! object
Therefore your code can be replaced with:
bar: construct/with body-of foo mumble

Resources