From: arne@vajhoej.dk   
      
   On 11/20/2025 6:54 AM, Dan Cross wrote:   
   > In article <10flqou$2l1qc$1@dont-email.me>,   
   > Arne Vajhøj wrote:   
   >> On 11/19/2025 11:02 AM, Dan Cross wrote:   
   >>> Further, by localizing the unsafety inside of the conversion,   
   >>> and using a return type that can represent failure, one can   
   >>> construct a _safe_ interface to do _unsafe_ conversions.   
   >>   
   >>> In contrast, `Valid` is easy to misuse, primarily by not using   
   >>> it at all, in which case you run the risk of raising a runtime   
   >>> exception, or just having an incorrect program. With either   
   >>> `Option` or `Result`, you are forced to contend with the error   
   >>> case. The programmer can't forget to check.   
   >>>   
   >>> Finally, `Valid` is very limited in its applicability: it can   
   >>> only be used with scalar types. At least in Rust, `Option` and   
   >>> `Result` are generic over essentially arbitrary types.   
   >>>   
   >>> Put another way, if the language supports something like an   
   >>> `Option` type, then there is no need for a special-case facility   
   >>> like Ada's `Valid` attribute.   
   >>   
   >> All fine.   
   >>   
   >> But you can do the same in Ada.   
   >   
   > Indeed, you can, but I never said you couldn't. My point was   
   > that Ada's `Valid` attribute is overly specific, underpowered,   
   > and ultimately unnecessary in a language that supports proper   
   > sum types over generics. Ada's `Valid` attribute is neither as   
   > general nor as robust as using an ADT like   
   > Option/Result/Maybe/whatever it's called in any given langauge.   
      
   Ada's valid does exactly what it is supposed to do: check if the   
   output from an Unchecked_Conversion meet the constraints of the   
   data type.   
      
   It is not a replacement for Option/Result.   
      
   It can be used to implement Option/Result for those   
   types that can be set with Unchecked_Conversion, but obviously   
   not for other data types.   
      
   A screwdriver is not good for hammering nails into wood,   
   but that does not mean that a screwdriver is a bad tool - it   
   is just not intended for that usage.   
      
   Regarding the risk of not being checked, then it is inherent   
   in the problem - no matter the mechanism used then the developer   
   can chose not to do it right, because "there will always be   
   a valid value here".   
      
   > Also not as general. Note that in the Rust example, `Option` is   
   > generic over some type `T`: your example is concrete over the   
   > color enum, and just colocates it with a flag and a convenience   
   > method that returns null if the flag is false (but now all   
   > accesses are via reference).   
      
   > But Ada supports generics (recall that Stepanov did the first   
   > implementation of the STL in Ada). You could use that to build   
   > an actual `Option` type that would closer to the Rust version.   
      
   Generics is one of those Ada features that I don't like to use.   
      
   But a generic version is attached below.   
      
   > It would probably not be as convenient to use because Ada lacks   
   > Rust-style pattern matching and destructuring, but it would more   
   > or less obviate the need for `Valid`.   
   Valid is still a way - probably the best way - to produce   
   the Option.   
      
   Arne   
      
   ++++   
      
   $ type OptionPackage.ads   
   generic   
    type T is private;   
      
   package OptionPackage is   
      
   type Option(Valid : Boolean := False) is record   
    case Valid is   
    when False =>   
    null;   
    when True =>   
    Value : T;   
    end case;   
   end record;   
      
   function SomeValue(O : T) return Option;   
      
   function None return Option;   
      
   end OptionPackage;   
   $ type OptionPackage.adb   
   package body OptionPackage is   
      
   function SomeValue(O : T) return Option is   
      
   res : Option;   
      
   begin   
    res := (Valid => True, Value => o);   
    return res;   
   end SomeValue;   
      
   function None return Option is   
      
   res : Option;   
      
   begin   
    res := (Valid => False);   
    return res;   
   end None;   
      
   end OptionPackage;   
   $ type ColorFul3.adb   
   with Unchecked_Conversion;   
      
   with Ada.Text_IO, Ada.Integer_Text_IO;   
      
   with OptionPackage;   
      
   use Ada.Text_IO, Ada.Integer_Text_IO;   
      
   Procedure ColorFul3 is   
      
   type Color is (Red, Green, Blue);   
   for Color use (Red => 1, Green => 2, Blue => 3);   
   for Color'Size use Integer'Size;   
      
   procedure Put(col : Color) is   
      
   begin   
    case col is   
    when Red => Put("Red");   
    when Green => Put("Green");   
    when Blue => Put("Blue");   
    end case;   
   end Put;   
      
   package ColorOption is new OptionPackage(T => Color);   
      
   function Integer2ColorNice(I : Integer) return ColorOption.Option is   
      
   res : ColorOption.Option;   
      
   begin   
    case I is   
    when 1 => res := ColorOption.SomeValue(Red);   
    when 2 => res := ColorOption.SomeValue(Green);   
    when 3 => res := ColorOption.SomeValue(Blue);   
    when others => res := ColorOption.None;   
    end case;   
    return res;   
   end Integer2ColorNice;   
      
   function Integer2ColorTricky(I : Integer) return ColorOption.Option is   
      
   function BitI2C is new Unchecked_Conversion(Source => Integer, Target =>   
   Color);   
      
   temp : Color;   
   res : ColorOption.Option;   
      
   begin   
    temp := BitI2C(I);   
    if temp'Valid then   
    res := ColorOption.SomeValue(temp);   
    else   
    res := ColorOption.None;   
    end if;   
    return res;   
   end Integer2ColorTricky;   
      
   col : ColorOption.Option;   
      
   begin   
    for I in 0..4 loop   
    col := Integer2ColorNice(I);   
    Put(I, Width => 1);   
    Put(" is ");   
    if col.Valid then   
    Put(col.Value);   
    else   
    Put("not a valid color");   
    end if;   
    New_Line;   
    end loop;   
    for I in 0..4 loop   
    col := Integer2ColorTricky(I);   
    Put(I, Width => 1);   
    Put(" is ");   
    if col.Valid then   
    Put(col.Value);   
    else   
    Put("not a valid color");   
    end if;   
    New_Line;   
    end loop;   
   end ColorFul3;   
      
   --- SoupGate-Win32 v1.05   
    * Origin: you cannot sedate... all the things you hate (1:229/2)   
|