Re: Weird Behaviour
Okay. Thanks!
Re: Weird Behaviour
According to Nim manual: [https://nim-lang.org/docs/manual.html#syntax-precedence](https://nim-lang.org/docs/manual.html#syntax-precedence) > Whether an operator is used a prefix operator is also affected by preceding > whitespace (this parsing change was introduced with version 0.13.0): > > echo $foo > > # is parsed as > > echo($foo) See how Nim parse your code: import macros dumpTree: var x = number.len-2 var y = number.len - 2 var z = number.len- 2 var a = number.len -2 Run Output: StmtList VarSection IdentDefs Ident "x" Empty Infix Ident "-" DotExpr Ident "number" Ident "len" IntLit 2 VarSection IdentDefs Ident "y" Empty Infix Ident "-" DotExpr Ident "number" Ident "len" IntLit 2 VarSection IdentDefs Ident "z" Empty Infix Ident "-" DotExpr Ident "number" Ident "len" IntLit 2 VarSection IdentDefs Ident "a" Empty Command DotExpr Ident "number" Ident "len" Prefix Ident "-" IntLit 2 Run
Re: Weird Behaviour
> var a = number.len -2 #throws invalid Indentation error That's because it is equivalent to `number.len(-2)` and/or `len(number, -2)` ; followed by something that looks like a negative number is a negative number (and not a "subtract" operation).
Re: Weird Behaviour
Make sure you are not having any extra whitespace at the end of lines. If not, then check for indentations and replace them with spaces. If done correctly, your program should work fine.
Weird Behaviour
`proc check_number(number: string): bool = var x = number.len-2 #works var y = number.len - 2 #works var z = number.len- 2 #works var a = number.len -2 #throws invalid Indentation error echo x, y, z, a return true echo check_number("1234") ` Run
Re: Weird Behaviour
Please format your question properly.
Re: Weird behaviour with generic type
> What do you think of this solution? I think it's a good solution.
Re: Weird behaviour with generic type
I think it is fine since `TestType`'s members are private. So, constructing a `TestType` from another module would require a call to `newTestType` anyway.
Re: Weird behaviour with generic type
Thank you all very much for the welcome and for all the help! I know see where the problem is, and I hope that, in the future, an easier syntax would work. In any case, I went with the following solution, using a generic type at the type level and specializing only in the procs' interface: type AbstractType = int or string #Fully generic type, but constrained in the procs interface. TestType*[T, Y] = object a : T b : Y proc newTestType[T : AbstractType, Y : AbstractType](a : T, b : Y) : TestType[T, Y] = return TestType[T, Y](a : a, b : b) proc print_test_type(t : TestType) = echo t.a echo t.b let t1 = newTestType(1, 2) t2 = newTestType("string", "here") t3 = newTestType(10, "mixed") echo typedesc(t1) print_test_type(t1) echo typedesc(t2) print_test_type(t2) echo typedesc(t3) print_test_type(t3) Run What do you think of this solution? The only problem here is that, to construct a TestType Run one should always use the newTestType Run proc in order to have the correct AbstractType Run behaviour.
Re: Weird behaviour with generic type
Welcome to Nim. You are actually touching an advanced part of the Nim type system, the `bind once` versus `bind many` part. **bind once vs bind many** Let's go through that with an example. proc foo1(x, y: SomeInteger) = echo "Ran foo" proc foo2(x: SomeInteger, y: SomeInteger) = echo "Ran foo" proc bar[T, U: SomeInteger](x: T, y: U) = echo "Ran bar" proc baz(x, y: distinct SomeInteger) = echo "Ran bar" let a = int32 10 let b = int64 20 block: # fails foo1(a, b) block: # fails foo2(a, b) block: # works bar(a, b) block: # works baz(a, b) Run In the first case `foo1`, from the signature it is intuitive that `x` and `y` should be of the same type. The second case `foo2` is equivalent to `foo1`, for generic instantiation, once `SomeInteger` has been inferred from the first parameter, it is applied verywhere else. It is called `bind once`. The main benefit is that both definition are interchangeable, and it seems to be a good default. There are 2 ways to ask the compiler to not bind once. Option 1 via indirection: in the `bar` function, `x` of type `T` which is instantiated to int32, and `y` of type `U` which is instantiated to int64. The `[T, U: SomeInteger]` declaration only restrict T and U to be of SomeInteger but never instantiates `SomeInteger` so it's not bind to any concrete type like in the 2 previous examples. Option 2 via explecitly asking `bind many` behaviour: in the `baz` function, the `distinct` keyword tells the compiler to lock the inference of `SomeInteger` to the type of `x`. **The tricky part 1: distinct solution** Hopefully the examples are simple enough and you managed to follow me until here. The key part to remember is that `distinct` tells the compiler to not `bind once`. You might think, oh I just need to use distinct for my types then. type AbstractType = int or string TestType*[T, Y : distinct AbstractType] = object Run Unfortunately, `distinct` has a completely different meaning in a type section. **``distinct`` types** Distinct types are well covered in the manual. It allows you to create units (for example physics units like Meter and Mile or currency like Dollar and Euro) and prevent you from mixing them at the type system level. Example type Dollar = distinct int Euro = distinct int # Let's borrow procedures from the base ``int`` type proc `+`(x, y: Dollar): Dollar {.borrow.} proc `+`(x, y: Euro): Euro {.borrow.} # And overload the print to add the unit proc `$`(x: Dollar): string = $int(x) & " dollars" proc `$`(x: Euro): string = $int(x) & " euro" let a = 10.Dollar let b = 3.Dollar let c = 5.Euro let d = 1.Euro echo a+b # Prints 13 dollars echo c+d # Prints 6 euros # Compile-time error "Type mismatch" # echo a+c Run **The tricky part 2: indirection solution** Since `distinct` doesn't work in a type section. You might think, well I can use the `bar` solution, bind `T` and `U` to different types. type AbstractType = int or string TestType*[T: AbstractType; Y: AbstractType] = object a : T b : Y proc newTestType[T, Y: AbstractType](a: T, b: Y) : TestType[T, Y] = return TestType[T, Y](a : a, b : b) proc print_test_type(t : TestType) = echo t.a echo t.b let t1 = newTestType(1, 2) t2 = newTestType("string", "here") t3 = newTestType(10, "mixed") echo typedesc(t1) print_test_type(t1) echo typedesc(t2) print_test_type(t2) echo typedesc(t3) print_test_type(t3) Run Well unfortunately it doesn't work, within a type section, all the generic T, Y types are `bind once`. **The solution** @thenjip nailed it, you need to shake the typesystem a bit until you "bind many indirections" directly at the type section level instead of the proc level: type AbstractType = int or string TestType*[T : distinct AbstractType | AbstractType; Y : distinct AbstractType | AbstractType] = object a : T b : Y proc newTestType[T, Y](a: T, b: Y) : TestType[T, Y] = return TestType[T, Y](a : a, b : b) proc print_test_type(t : TestType) = echo t.a echo t.b let t1 = newTestType(1, 2) t2 = newTestType("string", "here") t3 = newTestType(10, "mixed") echo typedesc(t1) print_test_type(t1) echo typedesc(t2) print_test_type(t2) echo typedesc(t3) print_test_type(t3) Run **Language design** More for future improvement of the language, I think the `distinct` keyword to create unit types and the
Re: Weird behaviour with generic type
> the generic type parameters of newTestType are not distinct They are unified at the type level in `TestType`. The (inferred) type is still `int or string` . Within `newTestType` , terms become available and obviously, type resolution happens at the term level. If this is intended or not might to be seen.
Re: Weird behaviour with generic type
It's actually enough to change just one of the type constraints to make it work: type TestType*[T : AbstractType; Y : distinct AbstractType | AbstractType] = object a : T b : Y Run That's nice, but the fact that the signature of `newTestType` _doesn 't_ have to be changed shows that either there's still something really wrong with Nim's type resolution or that it works in a way which so runs counter my intuition that it's not for me: the generic type parameters of `newTestType` are not `distinct`, hence `AbstractType` is bind once, so why does t3 = newTestType(10, "mixed") Run even compile?
Re: Weird behaviour with generic type
The compiler unifies `T` and `Y`, as long as it finds the same root type, `AbstractType` here. If you write down `AbstractType` again , e.g. using `AbstractType2`, then assigning with `Y`, the program should compile.
Re: Weird behaviour with generic type
Hello, Redefining `TestType` this way allows reusing `AbstractType`, but I find it a little repetitive: type TestType*[T : distinct AbstractType | AbstractType; Y : distinct AbstractType | AbstractType] = object a : T b : Y Run Also, welcome to Nim, @vitreo12 !
Re: Weird behaviour with generic type
> coming from Julia. Have missed that statement earlier this morning. So I wonder if you really intent what you are doing? For your TestType, you are not defining one single type, but four distinct different types. So maybe what you want is sum types, called object variants in Nim? For your compile problem, my guess is that for TestType*[T : AbstractType, Y : AbstractType] Run the compiler assumes that T and Y are identical, but you want then not identical, so the explicit "or types" compiles.
Weird behaviour with generic type
Hello everyone! I am a new Nim user (and, I have to say, I am pretty excited about this language!) coming from Julia. I was tesing some of Nim's capabilities when coming to generic types and procs, when I ran into this code not compiling: type AbstractType = int or string TestType*[T : AbstractType, Y : AbstractType] = object a : T b : Y proc newTestType[T : AbstractType, Y : AbstractType](a : T, b : Y) : TestType[T, Y] = return TestType[T, Y](a : a, b : b) proc print_test_type(t : TestType) = echo t.a echo t.b let t1 = newTestType(1, 2) t2 = newTestType("string", "here") t3 = newTestType(10, "mixed") echo typedesc(t1) print_test_type(t1) echo typedesc(t2) print_test_type(t2) echo typedesc(t3) print_test_type(t3) Run The compiler error is this: /home/francesco/Sources/nim/GenericTypes.nim(44, 21) template/generic instantiation of `newTestType` from here /home/francesco/Sources/nim/GenericTypes.nim(35, 20) Error: cannot instantiate TestType got: but expected: Run On the other hand, if I substitute TestType*[T : AbstractType, Y : AbstractType] Run with TestType*[T : AbstractType, Y : int or string] Run , the code correctly compiles and executes, resulting in the expected result: [system.int, system.int] 1 2 TestType[system.string, system.string] string here TestType[system.int, system.string] 10 mixed Run Does anyone know why this is the case?
Re: Understanding staticRead's weird behaviour
> 3 is a very bad idea, we did this for os.getConfigDir() which people then > used in a const section producing a binary that only works for the user who > compiled the program. People? Who? But in any case, with great power comes great responsibility. People that use Nim should know what `const` does. We shouldn't baby them into thinking it's just like a 'let'.
Re: Understanding staticRead's weird behaviour
Oh, thank you all. Didn't know that readFile requires ffi... Nim looks so easy but the deeper you dive the more complex it is.
Re: Understanding staticRead's weird behaviour
Dom96's answer no.1 is correct > 1\. Maybe --force flag will affect this? How can this be?, if Nim compiler detect that your .nim(on disk) not modified, and there is already .c/.o for the .nim in /nimcache, Nim compiler will skip codegen for the .c/.o file(if the dependencies/imports also not changed). Using staticRead(x) indeed change .nim file(in memory) if you change/update the 'x', but the compiler will not know about this(the compiler hash the file on disk, not the AST in memory). That's why, it looks cached somehow. Perhaps you can request a new feature for Nim compiler to hash the 'x' too as a dependency. No.3 Compile time FFI if I am not mistaken, araq has a branch of Nim implementing compile time FFI, but then abandoned/postponed because it would be a nightmare for maintainer to deal with upcoming issues related to compile time FFI while the Nim compiler itself already had many issues. But I believe, in the future we can revive this idea. It will be cool although a bit nasty.
Re: Understanding staticRead's weird behaviour
3 is a very bad idea, we did this for os.getConfigDir() which people then used in a `const` section producing a binary that only works for the user who compiled the program.
Re: Understanding staticRead's weird behaviour
No idea about `1`. Maybe `--force` flag will affect this? For `2`, your path is `app/version.txt` which suggests that it reads relative to your module. That makes sense to me. It's how `import` works. For `3`, Nim can distinguish. Nim cannot perform FFI at compile-time though, and `readFile` requires it. We could implement `readFile` for the Nim compile-time VM, and I would prefer to do this to be honest. Having a different API for compile-time vs. run-time is an additional burden on the programmer.
Understanding staticRead's weird behaviour
Hello nim lovers! Another portion of dumb questions from nim newbie 1) Consider this project structure: app.nimble app/ - version.txt # contains "0.1.1-3" - utils.nim utils.nim contains getVersion helper which just reads from file: proc getVersion*(versionFile:string): seq[string] = staticRead(versionFile).split('-') # static is required because I need this value at compile time app.nimble: const versionFile = "app/version.txt" # ... task version, "Get version": echo "Proc call: " & $(getVersion(versionFile)) echo "Inline call: " & $(staticRead(versionFile).split('-')) Now here is the magic: > nimble version Executing task version in .../app.nimble Proc call: @[0.1] <-- WTF?! Inline call: @[0.1.1, 3] Okay, it looks like calling staticRead is cached somehow when using a proc (cause I had "0.1" some time ago). But why, and how to avoid it? 2) My versionFile must be defined **relative to `utils.nim` location** because that's where staticRead resides. If I move utils somewhere else, I would need to change versionFile's path which is absolutely terrible! 3) Also, just curious why we have to use staticRead at all? Looks like const already marks expressions as compile-time: const constEval = contains("abc", 'b') # computed at compile time, no need to use staticContains or smth data = readFile("somefile") # <-- OOPS! doesn't work at compile time data = staticRead("somefile") # <-- will work Cannot nim distinguish between compile/runtime and call appropriate read function accordingly?