Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

mixin value causes typechecker to fail #7310

Open
krux02 opened this issue Mar 7, 2018 · 13 comments
Open

mixin value causes typechecker to fail #7310

krux02 opened this issue Mar 7, 2018 · 13 comments
Assignees
Labels
Error Messages stale Staled PR/issues; remove the label after fixing them Type Inference

Comments

@krux02
Copy link
Contributor

krux02 commented Mar 7, 2018

let theEnvironment = "default-env"

proc foobar_internal(env: string; arg: int): void =
  echo "form ", env, " called foobar with arg: ", arg

template foobar(arg: int): void =
  mixin theEnvironment
  foobar_internal(theEnvironment, arg)

foobar(1)
proc main(): void =
  let theEnvironment = "local-env"
  foobar(2)
main()

compilation error:

nim c -r /tmp/scratch.nim
Hint: used config file '/home/arne/proj/nim/Nim/config/nim.cfg' [Conf]
Hint: system [Processing]
Hint: scratch [Processing]
scratch.nim(11, 7) Error: type mismatch: got <string, int literal(1)>
but expected one of: 
proc foobar_internal(env: string; arg: int): void
  first type mismatch at position: 1
  required type: string
  but expression 'theEnvironment' is of type: None

expression: foobar_internal(theEnvironment, 1)

In the error it tells me that the first argument is of type string, but then later it tells it is of type None. That cannot be right.

@krux02 krux02 changed the title mixin causes typechecker to fail mixin value causes typechecker to fail Mar 7, 2018
@andreaferretti
Copy link
Collaborator

I am sure something is not right, but what does it even mean to mixin a string? mixin is usually used with procedure symbols to allow overloading (changing symbols from closed to open)

@Araq
Copy link
Member

Araq commented Mar 8, 2018

I think the compiler is right (though the error message is bad). mixin does not mean "look up in instantiation scope", it means "consider both current and instantiation scope" and so it picks up both symbols at the same time giving "theEnvironment| theEnvironment" the type None.

@krux02
Copy link
Contributor Author

krux02 commented Mar 8, 2018

@andreaferretti I am trying to do something like environment variables in unix. Or dynamic scoping like in emacs lisp. I just give you an example of this use case so you might understand it a bit better.

Take the random module for an example with it's random number generator. You want a simple to use rand function that just gives you a random number without setup. But you also might want to control the instance of a random number generator. With my pattern it would work like the following:

let a = rand()  # default global random number generator
let b = rand()  # default global random number generator
let randomenv = #[ init random number generator ]#
let c = rand() # now randomenv shadows the default random number generator
let d = rand() # again random number from randomenv

A second example where this might be useful is for memory allocation. Normally when you have a library that you call and you know it allocats a lot of junk temporary memory the garbage collector would be required to collect it all. With this pattern it would be possible to redirect the memory allocation to a memory pool for the library call and then just reset the memory pool.

let memoryenv = createMemoryPool(size = megabyte(32))
var accu = 0
for i in 0 ..< 1000:
  let myResult = libraryCallWithLotsOfTemporaryMemoryAllocation()
  memoryenv.reset  # Everything that still holds a pointer into the data of `memoryenv` is invalid
  accu += myResult

@Araq would there be something like look up in instanciation scope? Because when do the same pattern with just macros, I have no problems:

import macros

let theEnvironment = "default-env"

proc foobar_internal(env: string; arg: int): void =
  echo "form ", env, " called foobar with arg: ", arg

macro foobar(arg: int): typed =
  result = newCall(bindSym"foobar_internal", ident"theEnvironment", arg)

foobar(1)
proc main(): void =
  let theEnvironment = "local-env"
  foobar(2)
main()

# output: 
#
#   form default-env called foobar with arg: 1
#   form local-env called foobar with arg: 2

@andreaferretti
Copy link
Collaborator

andreaferretti commented Mar 8, 2018

@krux02 I understand what you want to do, but what mixin does is a different thing :-)

Scala has implicit parameters for that - they are both a blessing and a curse

@krux02
Copy link
Contributor Author

krux02 commented Mar 8, 2018

I know implicit parameters in Scala. Even though they bind by Type and not by name, they would do the trick here.

@Araq
Copy link
Member

Araq commented Mar 8, 2018

Use a .dirty template and maybe bind foobar_internal.

@krux02
Copy link
Contributor Author

krux02 commented Mar 9, 2018

Yes a dirty template works. Still I don't like the dirty template, because it is dirty 🙃. Yea I mean it binds all identifiers in instanciation scope and I can't decide which identifiers should come from that scope.

If there would be an instanciation scope mixin/bind, then also other use cases of the dirty pragma could be removed from the Nim code base.

@andreaferretti
Copy link
Collaborator

My suggestion would be to avoid dynamic variables like the plague, but i'ts your choice... :-D

@krux02
Copy link
Contributor Author

krux02 commented Mar 9, 2018

@andreaferretti yea are probably right. Though they are useful. And this is probably not the way to implement them.

@krux02
Copy link
Contributor Author

krux02 commented Mar 9, 2018

@Araq I just tried out the dirty pragma and it does not work:

# filename: "mymodule.nim"
let theEnvironment = "default-env"

proc foobar_internal(env: string; arg: int): void =
  echo "form ", env, " called foobar with arg: ", arg

template foobar*(arg: int): void {.dirty.} =
  bind foobar_internal
  foobar_internal(theEnvironment, arg)

# filename: "scratch.nim"
import mymodule

foobar(1)
proc main(): void =
  let theEnvironment = "local-env"
  foobar(2)
main()

error:

Hint: system [Processing]
Hint: scratch [Processing]
Hint: mymodule [Processing]
mymodule.nim(1, 5) Hint: 'theEnvironment' is declared but not used [XDeclaredButNotUsed]
scratch.nim(3, 7) template/generic instantiation from here
mymodule.nim(8, 19) Error: undeclared identifier: 'theEnvironment'

when I leave out the bind statement it has this error:
mymodule.nim(8, 3) Error: undeclared identifier: 'foobar_internal'

@Araq
Copy link
Member

Araq commented Mar 9, 2018

Huh, strange...

@krux02
Copy link
Contributor Author

krux02 commented Mar 10, 2018

I don't think it's strange at all. The dirty praga does resolve symbols at instanciation context. The problem is foobar_iternal is not exported and therefore in instanciation context it can't be resolved.

@krux02 krux02 self-assigned this Jan 14, 2019
@stale
Copy link

stale bot commented Aug 4, 2020

This issue has been automatically marked as stale because it has not had recent activity. If you think it is still a valid issue, write a comment below; otherwise it will be closed. Thank you for your contributions.

@stale stale bot added the stale Staled PR/issues; remove the label after fixing them label Aug 4, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Error Messages stale Staled PR/issues; remove the label after fixing them Type Inference
Projects
None yet
Development

No branches or pull requests

3 participants