-
Notifications
You must be signed in to change notification settings - Fork 23
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
RFC: Generic generic parameters #5
Comments
👍 for HKT! |
Note that higher kinded types are not as required since you can simply underspecify generic constraints in Nim (Nim's generics are close to C++ templates in this regard). |
The feature, while not required, is consistent with (my view of) the Nim way, works especially well with concepts, and increases the expresiveness of the type system. That you can get around their omission by specifying less strikes me as making a virtue of a necessity. Let's take the simplest example in pseudo-Nim
The meaning is clear, and the variations on this (allowing another generic array param to allow different arrays to back keys and values) are straightforward. |
Isn't this already the case? |
@Xe, no. The previous example is written in |
@Xe, the issue is not concepts, but "generic generic parameters", that is, using uninstantiated generics as the parameters of a generic, as A is in Map in the example. If you fix the syntax, the compiler will tell you: 'Error: no generic parameters allowed for A'. Concepts make this feature much nicer. |
I would love that. I have a base method declared with a generic type TT (TT is expected to be Container[T]) But in some implementations I need to get access to the T subtype of TT for proc like |
This works: type
Array[T] = concept a
a.len is Ordinal
#get(a, Ordinal) is T
for value in a:
value is T
Map[K, V] = concept m
type A = Array
m.keys is A[K]
m.values is A[V]
template mapImpl(A: typedesc): auto =
type MapImpl[Key, Value] = object
keys: A[Key]
values: A[Value]
assert MapImpl is Map
MapImpl
proc get[T](arr: openarray[T], idx: Ordinal): T = arr[idx]
type
SeqMap = mapImpl(seq)
iterator pairs(m: Map): (m.K, m.V) =
let ks = iterator(keys: type(m.keys)): m.K =
for k in keys:
yield k
let vs = iterator(values: type(m.values)): m.V =
for v in values:
yield v
while true:
let p = (ks(m.keys), vs(m.values))
if ks.finished or vs.finished: break
yield p
proc toSeqMap[K, V](ps: openarray[(K, V)]): SeqMap[K, V] =
result.keys = newSeq[K](ps.len)
result.values = newSeq[V](ps.len)
for i, p in ps.pairs:
(result.keys[i], result.values[i]) = p
var sm = {"a": 1, "b": 2}.toSeqMap
for k, v in sm:
echo "key: ", k, ", value: ", v Comment out the |
I believe that as people scorned generic types in the past (Java, Go) and have always ended up conceding in the end (and being rescued by Wadler) so one day languages that have said "we do not need higher-kinded generics" will realize that they ought to be a standard part of modern languages. Nim would do well to add itself to the (currently relatively short) list of (non-toy) languages that have this feature today. It would be a way of giving itself a boost (particularly with the functional community). |
Since 2016, Nim support for higher-kinded type is actually quite usable. The only limitation being generic generics in the proc signature but you can do proc foo[T](a: T) =
type U = T.U # Access the nested generics instead of having to do proc foo[T, U](a: T[U]) =
discard which is a ergonomic improvement. I.e. functionality is there, and this RFC improves ergonomics. |
@mratsim thanks for the clarification. Maybe just an ignorance of the syntax here, but This is probably just my ignorance. Where's the best place to read up on this? (Searching led me here.) |
It would come from the type declaration, assuming: type Foo[U] = object
field: U
proc foo[T](a: T) =
type U = T.U # Access the nested generics The best places to show examples of Nim higher-kinded types in use are probably:
In short Nim is a practical language to implement math concepts in Number Theory in a generic way combining higher-kinded types and dependent-types like capabilities. Bonus, you can use unicode if it makes sense: https://github.com/mratsim/constantine/blob/ec76ac5e/constantine/towers.nim#L29 type
Fp2*[C: static Curve] = object
c0*, c1*: Fp[C]
β = NonResidue
type
Fp6*[C: static Curve] = object
c0*, c1*, c2*: Fp2[C]
ξ = NonResidue
# We call the non-residue ξ on 𝔽p6 to avoid confusion between non-residue
# of different tower level
type
Fp12*[C: static Curve] = object
c0*, c1*: Fp6[C]
γ = NonResidue
# We call the non-residue γ (Gamma) on 𝔽p6 to avoid confusion between non-residue
# of different tower level |
@mratsim dependent types too! What!! I definitely need to dig more deeply into Nim than I have. Thanks so much for the explanation. |
This RFC is stale because it has been open for 1095 days with no activity. Contribute a fix or comment on the issue, or it will be closed in 30 days. |
Nim doesn't allow parameters of generic to be generic themselves. The lack of this feature has come up a few times, sometimes in the context of implementing something from functional programming like a
flatMap or monadic API, and also as with C++ in parameterizing containers. The usual workaround in the
absence of this feature is to pass in only instantiated types to generics.
This feature exists in C++, D, and Scala, and it is one of the highly desired missing features from Rust, see 'higher kinded types', or HKT, where it's perhaps more necessary than in Nim since Rust uses algebraic data types for errors rather than exceptions. Having this, variadic generics, and working static[T], and concepts will close the gap between Nim and C++ 17.
The text was updated successfully, but these errors were encountered: