Thread overview
Could that bug be catch using D's way?
Feb 19, 2018
Marc
Feb 19, 2018
Simen Kjærås
Feb 19, 2018
rikki cattermole
Feb 19, 2018
Simen Kjærås
Feb 19, 2018
Basile B.
Feb 19, 2018
Simen Kjærås
Feb 19, 2018
Basile B.
Feb 19, 2018
Ali Çehreli
February 19, 2018
I'm pretty sure something could be done with Ada's type range but what we could do using D?
February 19, 2018
On Monday, 19 February 2018 at 12:58:45 UTC, Marc wrote:
> I'm pretty sure something could be done with Ada's type range but what we could do using D?

We can easily define a range type in D. The simple example below probably has awful performance and many holes, but outlines the basic idea. It will not have compile-time bounds checks, but can be used as an int when it needs to, and enforces at run-time that the value is within the specified bounds.

import std.format : format;
import std.exception : enforce;

struct Range(T, T min, T max, T defaultValue = 0)
if (defaultValue >= min && defaultValue <= max)
{
    private T payload;

    this(T value)
    {
        this = value;
    }

    T get() { return payload; }
    alias get this;

    Range opBinary(string op)(T value)
    {
        return Range(mixin("payload "~op~" value"));
    }

    ref Range opOpAssing(string op)(T value)
    {
        this = Range(mixin("payload "~op~" value"));
        return this;
    }

    ref Range opAssign(T value, string file = __FILE__, int line = __LINE__)
    {
        enforce(value <= max && value >= min, format("Value needs to be between %s and %s, not %s.", min, max, value), file, line);
        payload = value;
        return this;
    }
}

unittest
{
    Range!(int, 3, 15, 3) a = 4;
    a = 16; // foo.d(38): Value needs to be between 3 and 15, not 16.
    int n = a;
}

It currently does no sensible testing of what operators it allows, and does not support unary operators. Other potential improvements include merging of bounds when the other operand is a range as well (so Range!(int, 3, 6) + Range!(int, 2, 8) returns a Range!(int, 5, 14)). Perhaps a more sensible default value would be min(maxValue, max(0, minValue)).

--
  Simen
February 19, 2018
On 19/02/2018 1:24 PM, Simen Kjærås wrote:
> On Monday, 19 February 2018 at 12:58:45 UTC, Marc wrote:
>> I'm pretty sure something could be done with Ada's type range but what we could do using D?
> 
> We can easily define a range type in D. The simple example below probably has awful performance and many holes, but outlines the basic idea. It will not have compile-time bounds checks, but can be used as an int when it needs to, and enforces at run-time that the value is within the specified bounds.
> 
> import std.format : format;
> import std.exception : enforce;
> 
> struct Range(T, T min, T max, T defaultValue = 0)
> if (defaultValue >= min && defaultValue <= max)
> {
>      private T payload;
> 
>      this(T value)
>      {
>          this = value;
>      }
> 
>      T get() { return payload; }
>      alias get this;
> 
>      Range opBinary(string op)(T value)
>      {
>          return Range(mixin("payload "~op~" value"));
>      }
> 
>      ref Range opOpAssing(string op)(T value)
>      {
>          this = Range(mixin("payload "~op~" value"));
>          return this;
>      }
> 
>      ref Range opAssign(T value, string file = __FILE__, int line = __LINE__)
>      {
>          enforce(value <= max && value >= min, format("Value needs to be between %s and %s, not %s.", min, max, value), file, line);
>          payload = value;
>          return this;
>      }
> }
> 
> unittest
> {
>      Range!(int, 3, 15, 3) a = 4;
>      a = 16; // foo.d(38): Value needs to be between 3 and 15, not 16.
>      int n = a;
> }
> 
> It currently does no sensible testing of what operators it allows, and does not support unary operators. Other potential improvements include merging of bounds when the other operand is a range as well (so Range!(int, 3, 6) + Range!(int, 2, 8) returns a Range!(int, 5, 14)). Perhaps a more sensible default value would be min(maxValue, max(0, minValue)).
> 
> -- 
>    Simen

https://dlang.org/phobos/std_experimental_checkedint.html#.Checked.min
February 19, 2018
On Monday, 19 February 2018 at 13:33:34 UTC, rikki cattermole wrote:
> https://dlang.org/phobos/std_experimental_checkedint.html#.Checked.min

Can't seem to get that to work, so I assumed it's not meant to be used that way:

import std.experimental.checkedint;

struct MyHook {
    enum min(T) = 3;
    enum max(T) = 15;

    static B onLowerBound(T, B)(T value, B bound)
    {
        assert(0);
    }

    static B onUpperBound(T, B)(T value, B bound)
    {
        assert(0);
    }
}

unittest
{
    Checked!(int, MyHook) a;
    a = 22;
    assert(a != 22); // This assert triggers, not the others.
}

--
  Simen
February 19, 2018
On Monday, 19 February 2018 at 13:51:50 UTC, Simen Kjærås wrote:
> On Monday, 19 February 2018 at 13:33:34 UTC, rikki cattermole wrote:
>> https://dlang.org/phobos/std_experimental_checkedint.html#.Checked.min
>
> Can't seem to get that to work, so I assumed it's not meant to be used that way:
>
> import std.experimental.checkedint;
>
> struct MyHook {
>     enum min(T) = 3;
>     enum max(T) = 15;
>
>     static B onLowerBound(T, B)(T value, B bound)
>     {
>         assert(0);
>     }
>
>     static B onUpperBound(T, B)(T value, B bound)
>     {
>         assert(0);
>     }
> }
>
> unittest
> {
>     Checked!(int, MyHook) a;
>     a = 22;
>     assert(a != 22); // This assert triggers, not the others.
> }
>
> --
>   Simen

I had never used Checked and i discover that strangely there's no hook for opAssign. onLowerBound and onUpperBound works for +=, -=, *=, /=, %=, ^^=, &=, |=, ^=, <<=, >>=, and >>>=. But since init is 0, += works:

struct MyHook {
    enum min(T) = 3;
    enum max(T) = 15;

    static B onLowerBound(T, B)(T value, B bound)
    {
        assert(0);
    }

    static B onUpperBound(T, B)(T value, B bound)
    {
        assert(0);
    }
}

unittest
{
    Checked!(int, MyHook) a;
    a += 16; // triggers the onUpperBound assert
}
February 19, 2018
On Monday, 19 February 2018 at 14:20:16 UTC, Basile B. wrote:
> I had never used Checked and i discover that strangely there's no hook for opAssign. onLowerBound and onUpperBound works for +=, -=, *=, /=, %=, ^^=, &=, |=, ^=, <<=, >>=, and >>>=. But since init is 0, += works:

Ah, thanks. Filed a bug:
https://issues.dlang.org/show_bug.cgi?id=18471

--
  Simen
February 19, 2018
On Monday, 19 February 2018 at 21:34:04 UTC, Simen Kjærås wrote:
> On Monday, 19 February 2018 at 14:20:16 UTC, Basile B. wrote:
>> I had never used Checked and i discover that strangely there's no hook for opAssign. onLowerBound and onUpperBound works for +=, -=, *=, /=, %=, ^^=, &=, |=, ^=, <<=, >>=, and >>>=. But since init is 0, += works:
>
> Ah, thanks. Filed a bug:
> https://issues.dlang.org/show_bug.cgi?id=18471
>
> --
>   Simen

I think this is a design choice, it's too obvious to be an omission (it's even documented that opAssign is like the ctor).

I'm curious to know what's the rationale though.
February 19, 2018
On 02/19/2018 05:33 AM, rikki cattermole wrote:

> https://dlang.org/phobos/std_experimental_checkedint.html#.Checked.min

Accompanying presentations:

  DConf 2017: https://www.youtube.com/watch?v=29h6jGtZD-U

  Google Tel Aviv: https://www.youtube.com/watch?v=es6U7WAlKpQ

Andrei likes the second one more.

Ali