It is sometimes convenient to define functions in a ad-hoc way from types to values. An example could be associating to each group or monoid its zero element. This can be done using typedesc as follows:
proc zero(x: typedesc[int]) = 0
proc zero(x: typedesc[float]) = 0.0
echo zero(float) # 0.0
Unfortunately, I am not able to abstract over this, and this is kind of a showstopper for me. Namely, I tried to define a few concepts like the following
type Monoid = concept x, y
x + y is type(x)
zero(type(x)) is type(x)
but nim fails to recognize, say, int as a Monoid. I have submitted a bug and even tried to do some work on it myself, but I was not able to fix it.
Is there some other way whereby I could associate to each type a value, in order to make progress in defining common algebraic structures?
I'm not sure if you need to do this kind of thing at runtime, but here's a way to do it at compile time using a relatively simple macro:
EDIT: I don't know if this is the best way, but it's the way that I first thought of. Anyway, hope it helps!
import macros
macro zero(typeName: typed): untyped =
# get the string representation of the
# name of the type passed in
var typeStr = $typeName.toStrLit()
template valLit(val) =
# template to just return the value
# passed into it so we don't have to
# write newNimNode(nnkIntLit) etc
val
# Any types you'd like can be put here
#
# getAst generates the ast of the
# Nim code inside of the template valLit
case typeStr
of "int":
result = getAst(valLit(0))
of "float":
result = getAst(valLit(0.0))
else:
result = getAst(valLit("No Value"))
echo zero(float)
echo zero(int)
echo zero(string)
HI @jyapayne, thank you for your suggestion. Unfortunately, that would not work, as one would have to know all possible monoid types beforehand. The approach with typedesc[T] allows for a piecemeal definition. Moreover, it does not take into account type resolution, only literals
let x = 3
echo zero(type(x)) # No Value
It also fails to be a monoid, which is the reason for this question
echo 3 is Monoid # false
Ah, okay, I think I understand a little better.
What about something like this? It's a little hacky, but it seems to work.
EDIT: I seemed to have missed the part about the piecemeal solution. But it works for the Monoid detection.
import macros
macro zero(typeName: typed): untyped =
let typeDesc = typeName.getType()
# BracketExpr
# Sym "typeDesc"
# Sym "float" <- this will always be the type
# get the string representation of the
# name of the type passed in
var typeStr = $typeDesc[1].toStrLit()
template valLit(val) =
# template to just return the value
# passed into it so we don't have to
# write newNimNode(nnkIntLit) etc
val
# Any types you'd like can be put here
#
# getAst generates the ast of the
# Nim code inside of the template valLit
case typeStr
of "int":
result = getAst(valLit(0))
of "float":
result = getAst(valLit(0.0))
else:
macros.error("Type '" & typeStr & "' is not a monoid.")
macro isMonoid(val: typed): untyped =
# Template to get boolean AST
template monoid(val) =
zero(type(val)) is type(val)
return getAst(monoid(val))
type Monoid = concept x, y
x + y is type(x)
x.isMonoid()
proc t(m: Monoid): string =
return $m
let x = 3
echo t(x) # prints "3"
echo zero(float) # prints "0.0"
echo zero(int) # prints "0"
echo zero(type(x)) # prints "0"