RFCs
RFCs copied to clipboard
Converted parameters with `to[T, toT]` types
Abstract
A proc can declare that some certain parameter of it can take arguments of different types and convert them to certain types.
Motivation
Sometimes converters are used to simplify calling some routines, like here. But now that converters are global, they sometimes cause troubles, like here, leading to ambigious calls.
Now that converters are frequently used only for some specific routines, why not make routines, instead of types, have converters? We can already do similar things with generic, auto
type, when
and overloading, but this RFC can make it simpler to write such a conversion.
Description
Parameters can have "converters" (not Nim converter
s). This is like varargs
, which can already convert arguments to certain types.
Like varargs
, we can make this a special type, to
:
proc foo(x: to[int]): int =
2 * x
# This is the same
proc foo(x: auto): int =
let x = int(x)
2 * x
# This is also the same
proc foo[T](x: T): int =
let x = int(x)
2 * x
It is also useful to convert with certain routines:
proc toAddr[I; T](a: array[I, T]): ptr T = addr(a[0])
proc useAddr[T](p: to[ptr T, toAddr]) =
discard # here p is ptr T
Like varargs
, overloaded procs are used on demand:
# StringBuilder is an imaginary object type
proc add(x: StringBuilder, s: to[string, `$`]) =
x.data &= s # if s is T, s is converted to string with (proc (T): string)`$`
If this proposal has downsides, I think:
- The semantic is not that readable. But with
varargs
already existing, it is still easy to understand. - It still requires editing every proc
- It is easily replaced by existing syntax.
Code Examples
No response
Backwards Compatibility
No response
This is already achievable with concepts:
type ToInt = concept x
int(x) is int
proc foo(x: ToInt): int = 2 * x
doAssert foo(2.Natural) == 4
###
type
ToString = concept x
string(x) is string
Stringlet = distinct string
proc add(x: var string; s: ToString) =
x &= string(s)
var s: string = "what follows was "
s.add("not a string".Stringlet)
doAssert s == "what follows was not a string"
This is already achievable with concepts:
type ToInt = concept x int(x) is int proc foo(x: ToInt): int = 2 * x doAssert foo(2.Natural) == 4 ### type ToString = concept x string(x) is string Stringlet = distinct string proc add(x: var string; s: ToString) = x &= string(s) var s: string = "what follows was " s.add("not a string".Stringlet) doAssert s == "what follows was not a string"
Ahh! idk that. fancy
This is already achievable with concepts:
type ToInt = concept x int(x) is int proc foo(x: ToInt): int = 2 * x doAssert foo(2.Natural) == 4 ### type ToString = concept x string(x) is string Stringlet = distinct string proc add(x: var string; s: ToString) = x &= string(s) var s: string = "what follows was " s.add("not a string".Stringlet) doAssert s == "what follows was not a string"
Wait, I just discovered that you are doing explicit converting with string(s)
. You don't need to do it with Natural
just because Natural
already supports being multiplied with int
s. Well... I know you can do explicit converting, but to[T, toT]
will let you write less code...
Related #168
. You don't need to do it with Natural just because Natural already supports being multiplied with ints.
this is also why we recommend to never use Natural
as input parameters (or indeed, any range
type) in production code since it performs an implicit narrowing conversion - an expression like let x = -1; let y = Natural(x)
will panic at runtime, as would the code in this proposal if the conversions happen to be narrowing.
Indeed, what this RFC lacks a way to deal with narrowing conversions safely - these are more common than one would thing, ie string-to-int can obviously fail, as can many other common ones - in other words, for this to fly, it would ideally be constrained to non-narrowing conversions (ie conversions that don't panic or raise exceptions)