zero-functional / zero-functional

A library providing zero-cost chaining for functional abstractions in Nim.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Support for type conversions via `.T`?

Vindaar opened this issue Β· comments

As far as I see it, there's currently no way to convert the type of e.g. a given sequence, no?
Like taking some seq[uint8] and outputting seq[int].
edit: of course I can use map manually for that, but that seems like unnecessary indirection.

I think for similar types this would be quite handy (thinking within SomeNumber). I hacked the following in to test

## Add item to seq, but converting from type U to T via `.T` call
proc zfAddItemConvert*[T, U: SomeNumber](a: var seq[T], idx: int, item: U) =
  discard(idx)
  a.add(item.T)

and within zfAddItemChk I put:

    elif compiles(zfAddItemConvert(`resultIdent`, `idxIdent`, `addItem`)):
      zfAddItemConvert(`resultIdent`, `idxIdent`, `addItem`)

which does just that.

I'm not sure though whether this is something that

  • should be handled by to as well
  • is desired at all.

Plus if the target is e.g. seq[seq[int]] from seq[seq[uint8]] it'd be non trivial, since instead of adding the individual seq, we'd now now have to iterate the innermost seq as well.

Hi @Vindaar
The template code is quite nifty and it works and it would be possible then to simply convert sequences of one numeric type into another one -
I would write the template like this:

proc zfAddItemConvert*[T, U](a: var Iterable[T], idx: int, item: U) =
  zfAddItem(a, idx, T(item))

so to say delegating the call to zfAddItem - supporting (hopefully) also the other types, such as array and DoublyLinkedList.

However: I am not sure if I really want to do that. So maybe not as default behavior and with an additional compile option. Yes, when doing a lot of conversions of lists this comes in handy, but under the hood I am not sure if the user is always aware of these conversions - because they could also happen accidentally (or couldn't they?). I am not entirely convinced right now - but think about it.

The other thing with converting seq of seq to another one - well I don't know if that is possible so easily.
At the moment - yes - one would have to use map for that - e.g.:

let bu = @[@[1u8, 2u8], @[3u8]]
proc conv(s: seq[uint8]): seq[int] = 
  s --> map(int(it)) --> to(seq[int]) # or s --> to(seq[int]) when that is supported
let bb = bu --> map(conv(it)) --> to(seq[seq[int]])

@Vindaar
I have added the modified code as suggested above - and had to do some fixes - and now it is possible to convert the above seq[seq[uint8]] to seq[seq[int]] with

let bu = @[@[1u8, 2u8], @[3u8]]
check(bu --> map(it --> to(seq[int])) 
             --> to(seq[seq[int]]) == @[@[1,2], @[3]])

I have created a branch "autoconvert" - if you think this is OK I will merge it to master

With the fixes it is now possible to use --> inside map and other zf commands

Oh, nice! I'll take a look at it later today.

However: I am not sure if I really want to do that. So maybe not as default behavior and with an additional compile option. Yes, when doing a lot of conversions of lists this comes in handy, but under the hood I am not sure if the user is always aware of these conversions - because they could also happen accidentally (or couldn't they?).

That was precisely what I also had in mind when I said I didn't know if it should be handled by to. Reason being I only came up with this idea when I had code like:

let a: seq[seq[uint8]] = returnsSomeSeq()
let b = a --> flatten() --> to(seq[int])

except that I didn't have the type explicitly declared for a. I was changing some data types from int -> uint8. That's when I thought it'd be really handy to have that feature (b was supposed to stay seq[int]), but it could also go very wrong, if I didn't have in mind that I'd be doing a type conversion at all!

So I agree with you. Maybe that feature should go under a different keyword? convert, convertTo?
edit: or maybe use as for this?

Just tested the autoconvert branch. Nice, works perfectly!
Especially nice is being able to use --> inside of map.

As I said in my last comment, maybe it's actually a good idea to allow type conversion only via a different name than to? asType would be another idea.

asType would be another idea.

+πŸ’―

My proposal is to add an additional boolean parameter to the to function (default is false) -
how could we name it? autoconvert,, autocast, trycast, force? But this is just a naming. When it is true the casting is added (of course this will only succeed when the cast itself is valid - such as numeric type to another numeric type).

For me it would be easier to implement - also I could maybe provide a better error message or a warning in case the 2nd parameter is false. So the user is made aware that something is wrong, but when adding the true he/she indicates "I know what I'm doing"...

Also for the user it is maybe easier to understand - compared to "when do I use to" and "when would I use asType"?

... just pushed it to this branch (including test)

Looks good, thanks! I'm happy either way (different keyword or using autoConvert as second argument).
You're right, maybe this way it's more approachable for new users.

I'll add a couple of comments to the commit.

OK πŸ‘
About your comments: I will add source code comments. Aaand - I found the issue - you mentioned it in #45
nim-lang/Nim#7375
... so this wasn't just bad style πŸ˜‰

Ah, sorry, I guess I wasn't clear.
I'm aware you did it because of the mentioned bug. My comments were meant to show a way to do it, while keeping static[bool] and still have it compile. I tested it with the changes and it compiles just fine. :)
The explicit bool and the newLit are the key.

overlooked that one πŸ™ˆ
OK - I hope that's all πŸ˜‰ and it's ready for integraton πŸ˜„