From: cross@spitfire.i.gajendra.net   
      
   In article <10flqou$2l1qc$1@dont-email.me>,   
   Arne Vajhøj wrote:   
   >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.   
      
   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.   
      
   >Both doing similar to above and by using Unchecked_Conversion   
   >and Valid.   
   >   
   > [snip code segment for brevity]   
   >   
   >A bit verbose. But if one want short code, then   
   >Ada is not the right language choice.   
      
   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). As a simulacrum, it will serve,   
   but one could do that in any number of languages; even C with a   
   couple of accessor functions can do something similar.   
      
   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.   
   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`.   
      
   >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.   
      
   Fortunately, Generics have been in the language since the 80s.   
   :-D   
      
    - Dan C.   
      
   --- SoupGate-Win32 v1.05   
    * Origin: you cannot sedate... all the things you hate (1:229/2)   
|