Re: Problem with UI notification system

2019-07-13 Thread Skaruts
... that moment when you realize you could've just used a tuple...


type DerpKind = enum
Derp1, Derp2, Derp3, Derp4, Derp5, Derp6

proc tup(b:bool):tuple =
if b: ( d:Derp1, i:10, s:"doo", c:'<', f:5.678 )
else: ( d:Derp4, i:100, s:"f00", c:'>', f:3.14 )

echo tup false  # (d: Derp4, i: 100, s: "f00", c: '>', f: 3.14)
echo tup true   # (d: Derp1, i: 10, s: "doo", c: '<', f: 5.678)


Run

:D


Re: Problem with UI notification system

2019-07-11 Thread Skaruts
@rayman22201 I see what you're saying. Tbh, personally I'm not an absolutist 
when comes to type safety. :) If I use the JSON option I'd make sure the fields 
are type hints (I suppose I could call it that). Something like 


a.text = "ndjfgnsj"
a.val = 10
a.enable = true


Run

Which is sort of my convention in dynamic languages.

I'll try around both options and see which one sticks. Meanwhile I managed to 
slightly improve the variant types system I already had.

Thanks everyone for the help.


Re: Problem with UI notification system

2019-07-10 Thread rayman22201
I edited my response above :-)

> the user checks which type of action it is, before it tries to access any 
> fields

Yeah. This is the crux of the problem. There is no other way around it. The key 
is making that check as robust as possible so it doesn't blow up when you don't 
expect it to :-)

In Nim, that basically means Variant types + case statements.


Re: Problem with UI notification system

2019-07-10 Thread Skaruts
That problem is true with python dictionaries too, btw. I think it's up to the 
user to know which fields to deal with. Or at least I'm running with that 
notion, that the user checks which type of action it is, before it tries to 
access any fields. For example:


proc handle_actions() =
var action:Action
while messenger.poll_actions(action):# loop the stack
case action.kind
of CellChangeSize:   resize_cells( action.dir )
of LayerAddRemove:   layer_add_rem( action.add )
of LayerToggleVisible:   layer_toggle_vis( action.vis )
of LayerRename:  layer_rename( action.new_name )
of LayerChangeAlgo:  layer_change_algo( action.algo )
else:discard


Run


Re: Problem with UI notification system

2019-07-10 Thread rayman22201
Templates are very powerful, and it's cool that Nim can do this. Nim is all 
about flexibility :-)

> in case an empty Action is needed, or to not have to type the whole 
> properties: %*{} thing (might be redundant, I was just trying out things and 
> stumbled on that. Still trying to grasp templates).

Yes. You could make your own template that does the Actions( properties:... ) 
stuff for you.


# I used &&, but you could use whatever you wanted.
template `&&`(fields: untyped): untyped =
  Action( properties: %*`fields` )

var a2 = &&{
  "layer": 1,
  "add": true,
  "vis": false,
  "new_name": "woohoo less typing!"
}


Run

But, here are some examples on why the json dictionary type is not as type safe 
(dynamically typed):


# assume all the code @mratsim wrote above is included

proc doSomething(a: Action) =
  if a.Foo != 42:
echo "sorry, not the answer to the universe"

var a1 = Action(
  properties: %*{
"foo" = 10
  }
)
var a2 = Action(
  properties: %*{
# oops, I'm missing `foo`
"bar" = "I'm a string"
  }
)
var a3 = Action(
  properties: %*{
"foo" = "oops, I'm a string now!"
  }
)

doSomething(a1) # cool
doSomething(a2) # uh-oh, I don't a have a bar, but the compiler won't tell 
me, I will get a run time Exception!
doSomething(a3) # even worse! I won't get any error, but the result will be 
wrong! a3.foo will be interpreted as 0 and just keep going! Have fun debugging 
this one in a large program.


Run

A Variant type on the other hand will yell at you at compile time that the 
variant is either missing the field or the field is the wrong type.

As far as the fix for Variant types go: I don't really care what syntax we end 
up going with, but I do hope it will get fixed eventually. It's more of an 
inconvenience imo. Variant types are still usable and capable without this 
feature! 


Re: Problem with UI notification system

2019-07-10 Thread Skaruts
Well meanwhile I managed to find [a bit of a 
workaround](https://gist.github.com/Skaruts/11c9b4b8af153c5a4664eb46300455f4) 
for not being able to have attributes with the same name, for another part of 
my project.


Re: Problem with UI notification system

2019-07-10 Thread Skaruts
@rayman22201 indeed I wasn't thinking of anything that isn't type safe or 
outside of Nim's phylosophy. What @mratsim did above (1st post) with templates 
and the JSON module is what got me wondering, since it's actually not that far 
away from behaving like a python dictionary, from the POV of the user.


# Correction: it's actually closer to the behavior of GDScript dictionaries,
# since afaik python doesn't allow creating dict fields like:
a.foo = 10

# only like:
a["foo"] = 10


Run


Re: Problem with UI notification system

2019-07-10 Thread rayman22201
@mratsim's solution is very good for prototyping, and probably what you want. 
The Json module is the recommended way to get python dictionary behavior in 
Nim. It's the generally recommended way to transition Python programmers to Nim 
:-P

> if one couldn't come up with a Nim implementation of the exact behavior of 
> python dictionaries?

But, a native Python dictionary is probably never going to be put in the std 
library. Nim is a **statically typed language** , and this is dynamic behavior! 
You are setting the types of the dictionary at runtime. This is against the 
philosophy of statically typed languages like Nim.

Dynamic types like this have a cost. It's still much faster than Python, but it 
isn't free, and you loose most of the Nim type safety.

Variant types like in your initial post is the most idiomatic Nim, type safe 
way to do it. In fact, this is how Nim does it for the compiler: 
[https://nim-lang.org/docs/manual.html#types-object-variants](https://nim-lang.org/docs/manual.html#types-object-variants)

Unfortunately, as @doofenstein pointed out, you have hit a limitation of 
variant types in the current Nim. Either every variant that shares a field must 
be grouped together, or each variant must have a different field name. This is 
slightly inconvenient, I agree, and the proposed fix has stalled unfortunately 
(bigger fish to fry in the compiler).

The advantage of variant types is that the compiler can warn you at compile 
time if you attempt to access a field on an Action that doesn't exist for that 
action type. You won't get that with a Dictionary. You (or your user) will get 
a run time error instead (this is the point of static types).

One strategy may be to use the Json dictionary while you are prototyping, then 
once you have a better idea of what fields you need on each Action type, swap 
it out for a Variant type. Personally, I prefer to just use Variant types up 
front, because it lets me more clearly design my data types, and lets the 
compiler help me (by yelling at me) if I forgot a field somewhere. 


Re: Problem with UI notification system

2019-07-10 Thread doofenstein
looking at the problem as you detailed it in your first post it seems like 
you're searching for a way to have variant object, where fields are shared 
between specific cases. There has been 
[discussion](https://github.com/nim-lang/RFCs/issues/19) to implement this, 
though they've come to a standstill.

What other have suggested is making the fields completely dynamic, which is not 
ideal, since it undermines type safety and is slower than a simple field. 
Though in this case it's probably the best solution.


Re: Problem with UI notification system

2019-07-10 Thread Skaruts
There's a lot to take in in this, and I'm understanding some of it, but I'll 
take some more time to see if I can grasp it mostly. There's quite a few things 
that are new or still mysterious to me.

I noticed I can sort of shortcut it with 


template action():untyped = Action( properties: %*{} )
var a = action
a.algo = 10
echo a.algo# 10


Run

in case an empty Action is needed, or to not have to type the whole 
**properties: %*{}** thing (might be redundant, I was just trying out things 
and stumbled on that. Still trying to grasp templates).

Meanwhile, I am wondering -- and this is just a curiosity; I don't intend to 
explore this path myself -- if with some clever use of templates like this, and 
assuming one can overload the {} operators to this end, if one couldn't come up 
with a Nim implementation of the exact behavior of python dictionaries? 


# basically so that one could do just this:
let a = {
some_str:"bla bla",
some_int:10
}
# or like this
var a = {}
a.derp = 10
a.depier = "the derpest"

Run


Re: Problem with UI notification system

2019-07-10 Thread mratsim
They allow rewriting a field access or field assignment call to the `Action` 
type to something else, there is more detail in the manual: 
[https://nim-lang.org/docs/manual_experimental.html#special-operators-dot-operators](https://nim-lang.org/docs/manual_experimental.html#special-operators-dot-operators).

In the example I'm using the template to instead access a properties json field 
and make those access/assignments feel seamless.

There is a remaining issue that the type returned is a `JsonNode` so in 
practice you would need:


a.new_name.getStr() # extract the string from the JsonNode
a.algo.getInt() # extract the int from the JsonNode


Run

If you want truly seamless calls without the need of `getStr` you would need 
something like the following, note the doAssert on the return type to make sure 
we don't get JsonNodes, and we don't need the . experimental template either:


import json

type
  Action = ref object
properties: JsonNode

template addPseudoField(A: typedesc, field: untyped, T: typedesc) =
  # A is for the type namespace (Action)
  
  template getType(args: varargs[untyped]): untyped =
# Alias the JsonNode getType function
when T is string:
  getStr(args)
elif T is int:
  getInt(args)
elif T is bool:
  getBool(args)
elif T is float:
  getFloat(args)
else:
  # You can add your own type special marshaller/deserializer
  raise newException(ValueError, "Unsupported type: " & $T)
  
  proc `field`*(a: A): T =
a.properties[astToStr(field)].getType()
  
  proc `field=`*(a: A, val: T) =
a.properties[astToStr(field)] = %val

Action.addPseudoField(layer, int)
Action.addPseudoField(add, bool)
Action.addPseudoField(vis, bool)
Action.addPseudoField(new_name, string)
Action.addPseudoField(algo, int)

var a = Action(
  properties: %*{
"layer": 0,
"add": true,
"vis": false,
"new_name": "fancy_name"
  }
)

echo a.new_name # "fancy_name"

a.algo = 10
echo a.algo # 10

doAssert a.algo is int
doAssert a.new_name is string


Run


Re: Problem with UI notification system

2019-07-10 Thread Skaruts
Hmm, I was just now reading about the JSON module and contemplating using it. 
Thanks for the examples.

I don't understand what the templates do though. 


Re: Problem with UI notification system

2019-07-09 Thread mratsim
Here is an example with a Json dictionary.


import json

{.experimental: "dotOperators".}

type
  Action = ref object
properties: JsonNode

template `.`(action: Action, field: untyped): untyped =
  action.properties[astToStr(field)]

template `.=`(action: Action, field, value: untyped): untyped =
  action.properties[astToStr(field)] = %value

var a = Action(
  properties: %*{
"layer": 0,
"add": true,
"vis": false,
"new_name": "fancy_name"
  }
)

echo a.new_name # "fancy_name"

a.algo = 10
echo a.algo # 10


Run

You can store and deserialize more complex types as strings with the [marshal 
module](https://nim-lang.org/docs/marshal.html) or as JsonNode with the [to 
macro in JSON 
module](https://nim-lang.org/docs/json.html#to.m%2CJsonNode%2Ctypedesc)


Problem with UI notification system

2019-07-09 Thread Skaruts
Sorry for the title being vague, but as it often happens to me, I really don't 
know how to summarize this...

I'm trying to make a messaging/notification system for ui elements to tell the 
data what to do. To make those notifications, I'm using that trick with enum 
and case/of.


type
ActionKind = enum
   LayerToggleVisible #, (...)

Action = ref object
layer:int# <-- more on this below
case kind:ActionKind
of LayerToggleVisible:  vis:bool
# (...)

# and then elsewhere I build a ui notification like you would expect
Action(
kind:LayerToggleVisible,
layer:index,
vis:false
)


Run

There's a few problems: not all kinds care about the **layer** index. I know I 
could do this...


# (...)
of LayerAddRemove, LayerToggleVisible, LayerRename, LayerChangeAlgo:
layer:int


Run

... but some of those kinds have to contain other values of their own, so afaik 
I can use that. So I just made the **layer** index exist for all kinds, and 
made different identifiers for each case. It would be handy to have overlapping 
fields, but like the layer field, they don't all converge in the same kinds. 


case kind:ActionKind
of LayerAddRemove: add:bool # bool  ( these two could
of LayerToggleVisible: vis:bool # boolgo together )
of LayerRename:new_name:string  # string
of LayerChangeAlgo:algo:int # int


Run

In python I would use dictionaries, because they allow creating arbitrary 
fields of arbitrary types in them on the fly. I wonder if something can be done 
to make this mimic that behavior more closely.

Ultimately I could just define a type with a whole bunch of fields and use them 
or ignore them as needed. Something like:


Action = ref object
name:string#<-- name would be used in a case/of, to check what 
action was requested and the respective value to check
layer:int
b:bool
i:int
s:string
pt:tuple[x:int,y:int]
new_name:string
algo:Algorithm
add:bool
vis:bool


Run

Would this be inadvisable?