On Friday, 6 September 2019 at 20:02:35 UTC, Bob4 wrote:
Hi,
I'm coming from a background in Python, without a lot of
experience in statically typed languages, and I'm struggling to
see how to make a certain function in D.
This is what I have in Python:
```
from typing import Union
Number = Union[int, float]
def clamp(value: Number, mini: Number, maxi: Number) -> Number:
if value >= maxi:
return maxi
if value <= mini:
return mini
return value
```
In Python, I could supply this function with any type really;
it doesn't type check, and essentially only runs
`value.__ge__(maxi)` and `value.__le__(mini)`. How can I
simulate this in D? There are already implementations of
`clamp` out there, but that isn't the point.
I hear that there is a "variant" type, that allows one to pass
any type; but I either want to restrict it to certain types, or
only to types that possess some kind of `__ge__` or `__le__`
method. The only way I found on how to do the former was
overloading, like:
```
bool test(int a) {...}
bool test(float a) {...}
```
Unfortunately this doesn't seem to work well with the return
type, and I feel like it's wrong to rewrite identical functions
over and over.
Do you know algebra?
You know, x?
x has a type. That is, it is a class that represents all
numbers(usually reals at least but sometimes it's integers or
complex or vectors or whatever).
In statistically typed languages one must always specify the type
int foo(float x)
In a dynamically typed language one does not have to specify the
type but then one can corrupt data by treating the value as the
wrong type. So effectively one has to do static typing in their
mind and keep track of everything. Dynamic typing simply allows
one to avoid verbosity because they can reuse a variable and
treat it differently... the cost is safety.
In meta programming, one can sorta do both:
A foo(T)(T x)
Here T stands for the type, but it is like x in algebra but
instead of numbers it can be types.
So T can be any type and so it is sorta like dynamic typing... so
what's the point then?
Well, T can't necessarily be anything and one can check the type
using meta programming.
What happens is that the compiler can determine what T is suppose
to be by how the programmer is using it and this then allows it
to revert to static typing internally and get all the safety:
e.g.,
int x;
foo(x);
double y;
foo(y)
Now we know that T is being treated as an int in the first case
and double in the second.
The type is transitive, meaning that the compiler can trace out
the type from the call/usage.
Inside foo, though, T is sorta like being dynamic and we have to
be more careful by telling the compiler what exactly we want T to
do.
So, we have dynamic <---meta> static
and meta sits in between. It gives us the best of both worlds
because we can be dynamic and static all in the right places(it's
not totally dynamic but as close as one can get and still be
fully dynamic).
Dynamic is totally typeless static is fully typed and meta allows
us to finagle between them to some degree. The cost is that we
have to write more complex code to deal with the typing.
Number = Union[int, float]
def clamp(value: Number, mini: Number, maxi: Number) -> Number:
if value >= maxi:
return maxi
if value <= mini:
return mini
return value
Your number is a union of two types. Either int or float.
In D, you do the same thing a number of ways(there is the
algebraic type which looks to be the same as Union).
auto clamp(A,B,C)(A a, B b, C c)
{
static if (is(A == int)) { // evaluated only if A is an int)
}
So now one has deal with what A really is inside the function and
so one need extra code...
clamp(3, 5.0, "asdf");
inside clamp A is int, B is real, and C is string FOR THAT CALL
ONLY!
If we call clamp again:
clamp(5.0, "asdf", new C);
Now A is a real, B is a string, and C is a C class type.
The two functions may look the same but in fact the compiler
essentially builds two different functions. We essentially have
to combine all the code... or we could overload and ignore meta
programming.
In any case, it is not very difficult, it just seems complicated.
If you are used to programming in one language you will have to
get used to another. The thing is, if you actually can program
then it is really nothing new... you do all of it in your head or
use the language to do the same thing all these languages are
Turing complete and so nothing is new under the sun.