Re: Function Arguments with Multiple Types

2019-09-07 Thread Bert via Digitalmars-d-learn

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.









Re: Function Arguments with Multiple Types

2019-09-06 Thread Ali Çehreli via Digitalmars-d-learn

On 09/06/2019 01:46 PM, Bob4 wrote:

> Thanks; this works, but I'm not sure why. Where does `T` come from?

Well... I assumed this would make you research templates. ;) Here is one 
resource: http://ddili.org/ders/d.en/templates.html


(T) means "this function is for any type; and I call that type T in the 
implementation."


> notice that I can change it to something else, like `ABC`, but I'm not
> sure why I need a `(T)` prepending the arguments.

Templates have two parameter lists; the compile-time parameter list 
comes first. You can call type parameters anything that makes sense; T 
is very common.


> What's the best way to type check the variables?

With template constraints: 
http://ddili.org/ders/d.en/templates_more.html#ix_templates_more.constraint,%20template


> I tried to use prepend
> `assert(cast(T)value !is null);` to the beginning of the function, but
> that didn't work.

That would bring an implicit requirement to your template: The type 
would have to be usable with the !is operator. (Template constraints 
make such requirements explicit.)


> I tried this, too, but it didn't work either (in fact it seemed to
> ignore it entirely when I replaced `T` with this):
>
> ```
> template Number(T)
>  if (is(T == float))
> {}
> ```

That should work, meaning "enable this template only if T is 'float'" 
(which would defeat the purpose of a template but I understand that it's 
a test.)


Ali



Re: Function Arguments with Multiple Types

2019-09-06 Thread Bob4 via Digitalmars-d-learn

On Friday, 6 September 2019 at 20:16:58 UTC, Ali Çehreli wrote:

On 09/06/2019 01:02 PM, Bob4 wrote:

> I feel like it's wrong to rewrite identical functions over
and over.

Enter templates. :)

auto clamp(T)(T value, T mini, T maxi) {
  if (value >= maxi) {
return maxi;
  }
  if (value <= mini) {
return mini;
  }
  return value;
}

unittest {
  assert(clamp("k", "d", "y") == "k");
  assert(clamp(42, 10, 20) == 20);
  assert(clamp(0.5, 1.5, 1_000) == 1.5);
}

void main() {
}

You can improve the function with template constraints.

Ali


Thanks; this works, but I'm not sure why. Where does `T` come 
from? I notice that I can change it to something else, like 
`ABC`, but I'm not sure why I need a `(T)` prepending the 
arguments.


What's the best way to type check the variables? I tried to use 
prepend `assert(cast(T)value !is null);` to the beginning of the 
function, but that didn't work.


I tried this, too, but it didn't work either (in fact it seemed 
to ignore it entirely when I replaced `T` with this):


```
template Number(T)
if (is(T == float))
{}
```


Re: Function Arguments with Multiple Types

2019-09-06 Thread Ali Çehreli via Digitalmars-d-learn

On 09/06/2019 01:02 PM, Bob4 wrote:

> I feel like it's wrong to rewrite identical functions over and over.

Enter templates. :)

auto clamp(T)(T value, T mini, T maxi) {
  if (value >= maxi) {
return maxi;
  }
  if (value <= mini) {
return mini;
  }
  return value;
}

unittest {
  assert(clamp("k", "d", "y") == "k");
  assert(clamp(42, 10, 20) == 20);
  assert(clamp(0.5, 1.5, 1_000) == 1.5);
}

void main() {
}

You can improve the function with template constraints.

Ali



Function Arguments with Multiple Types

2019-09-06 Thread Bob4 via Digitalmars-d-learn

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.