home bbs files messages ]

Forums before death by AOL, social media and spammers... "We can't have nice things"

   comp.os.vms      DEC's VAX* line of computers & VMS.      264,096 messages   

[   << oldest   |   < older   |   list   |   newer >   |   newest >>   ]

   Message 263,801 of 264,096   
   =?UTF-8?Q?Arne_Vajh=C3=B8j?= to Dan Cross   
   Re: Unsafe code blocks   
   19 Nov 25 20:26:54   
   
   From: arne@vajhoej.dk   
      
   On 11/19/2025 11:02 AM, Dan Cross wrote:   
   > In article <10fig0p$1n41a$2@dont-email.me>,   
   > Arne Vajhøj   wrote:   
   >> On 11/15/2025 9:16 PM, Dan Cross wrote:   
   >>>> Also note the availability of the 'Valid attribute to make sure that what   
   >>>> is in the variable after the unsafe conversion is actually a valid value.   
   >>>   
   >>> Sum types make this trivial:   
   >>>   
   >>>       impl SomeType {   
   >>>           fn try_from(i: i32) -> Option {   
   >>>               // if valid, return `Some(whatever`),   
   >>>               // else return `None`.   
   >>>           }   
   >>>       }   
   >> (assuming Option in Rust is what it is in other languages)   
   >>   
   >> Option and Ada Valid are somewhat different.   
   >>   
   >> Option is a way for a function/method to either return   
   >> a value or return the fact that there is no value.   
   >>   
   >> A much better way to do that than traditional   
   >> return null or -1 or whatever to indicate there   
   >> is no value.   
   >>   
   >> Ada valid attribute is a runtime check on the result from   
   >> an unsafe conversion to see if it meets the constraints   
   >> of the data type.   
   >   
   > I'm afraid this misses the point.   
   >   
   > If a language supports sum types, then general solutions like   
   > `Option` or `Result` types can be employed to represent the   
   > return type of the conversion operation.  If the conversion is   
   > invalid, then one returns `None` (or, perhaps, the `Err` variant   
   > of a `Result` if one wants to capture what the actual failure   
   > was).  That is, the conversion operation itself subsumes the   
   > functionality of the valid attribute, and both the value and   
   > whether the conversion was valid are represented in the return   
   > type.   
   >   
   > Again, taking Rust just as an example, there is a standard way   
   > to represent such conversions; the `TryFrom` trait (and it's   
   > dual, `TryInto`).  Consider this silly example problem: I have a   
   > type representing colors; perhaps the only colors I care about   
   > are Red, Green, and Blue. I can label these with integers, say   
   > 1, 2 and 3, and I can represent this in Rust using an   
   > enumeration.   
   >   
   > Now further suppose that I have a file of 8-bit bytes that   
   > contain the (raw, integral) representation of these color   
   > values; I'd like to convert these into proper objects of the   
   > enumeration type, but of course, not all 8-bit byte values   
   > represent valid colors, so the proper tool in this case is   
   > `TryFrom`.  Note, however, that converting back, from a color to   
   > its 8-bit value, is _always_ valid, so I just use `From` in that   
   > case (which also, conveniently, gives me `into`).   
      
   > #[derive(Clone, Copy, Debug, Eq, PartialEq)]   
   > enum Colors {   
   >      Red = 1,   
   >      Green = 2,   
   >      Blue = 3,   
   > }   
   >   
   > impl TryFrom for Colors {   
   >      type Error = String;   
   >      fn try_from(raw: u8) -> Result {   
   >          match raw {   
   >              1 => Ok(Self::Red),   
   >              2 => Ok(Self::Green),   
   >              3 => Ok(Self::Blue),   
   >              _ => Err(format!("unsupported value '{raw}'")),   
   >          }   
   >      }   
   > }   
      
   > Note the unit tests.  Note also, that, as a result of the way   
   > that the conversion is structured, as aided by the return type,   
   > the conversion operation itself is safe.   
   >   
   > Of course, this example is trivial, but the same principle   
   > applies for more comple conversions as well, including unsafe   
   > conversions.   
   >   
   > 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.   
      
   Both doing similar to above and by using Unchecked_Conversion   
   and Valid.   
      
   with Unchecked_Conversion;   
      
   with Ada.Text_IO, Ada.Integer_Text_IO;   
      
   use Ada.Text_IO, Ada.Integer_Text_IO;   
      
   Procedure ColorFul 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;   
      
   type ColorOption(Valid : Boolean := False) is record   
       case Valid is   
          when False =>   
             null;   
          when True =>   
             Value : Color;   
       end case;   
   end record;   
      
   function Integer2ColorNice(I : Integer) return ColorOption is   
      
   res : ColorOption;   
      
   begin   
       case I is   
           when 1 => res := (Valid => True, Value => Red);   
           when 2 => res := (Valid => True, Value => Green);   
           when 3 => res := (Valid => True, Value => Blue);   
           when others => res := (Valid => False);   
       end case;   
       return res;   
   end Integer2ColorNice;   
      
   function Integer2ColorTricky(I : Integer) return ColorOption is   
      
   function BitI2C is new Unchecked_Conversion(Source => Integer, Target =>   
   Color);   
      
   temp : Color;   
   res : ColorOption;   
      
   begin   
       temp := BitI2C(I);   
       if temp'Valid then   
           res := (Valid => True, Value => temp);   
       else   
           res := (Valid => False);   
       end if;   
       return res;   
   end Integer2ColorTricky;   
      
   col : ColorOption;   
      
   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 ColorFul;   
      
   A bit verbose. But if one want short code, then   
   Ada is not the right language choice.   
      
   I am printing instead of asserting, because assertions was first   
   to Ada 20 years ago and the VMS GNAT I have is over 25 years old.   
      
   Arne   
      
   --- SoupGate-Win32 v1.05   
    * Origin: you cannot sedate... all the things you hate (1:229/2)   

[   << oldest   |   < older   |   list   |   newer >   |   newest >>   ]


(c) 1994,  bbs@darkrealms.ca