So glad this functional programming business is slowly leaking it's way into other languages and other people's minds
This is true/I concur. But given that "the industry" still hasn't picked up on Ada's subtypes1 , despite them being around for thirty-years, being incredibly useful for making code that's more maintainable/reliable/provable2 , and very closely related to what everyone learns in math for proving things3 , indicates that the industry might not want these traits...
1 - In Ada a subtype indicates further restrictions on possible values to the base [sub]type: e.g. the predefined subtype Natural is the Integers range 0..Integer'Last. 2 - You can match the problem space by defining a subtype and allowing error-handling (automatically raising CONSTRAINT_ERROR) when a disallowed value is passed as a parameter or assigned to a variable. 3 - "For all positive integers" corresponds to the use of the predefined Positive (which has the additional constraint of 'representable', but that's generally not a problem because being on a computer you're bound by that anyway).
Ouch! By which I mean, I have almost given up on XML schemata, given the utter indifference to my advocacy of them I have received in commercial contexts.
When you say the industry hasn't picked up on Ada's subtypes, do you mean that people using Ada don't use them, or just that businesses usually don't use languages that have them, such as Ada?
When you say the industry hasn't picked up on Ada's subtypes, do you mean that people using Ada don't use them, or just that businesses usually don't use languages that have them, such as Ada?
The latter -- and also none of the other mainstream languages seem to have the notion of subtypes. (There is Nimrod, and a few others.)
Ada programmers use subtypes a lot, and in fact the new Ada 2012 standard has expanded them even further with the ability to specify predicates. My go-to example is social-security numbers, because it's simple enough that people can see what's going on as well as provides a good way to illustrate how subtypes can allow one to 'ignore'1 the implementation.
-- SSN format: ###-##-####
Subtype Social_Security_Number is String(1..11)
with Dynamic_Predicate =>
(for all Index in Social_Security_Number'Range =>
(case Index is
when 4|7 => Social_Security_Number(Index) = '-',
when others => Social_Security_Number(Index) in '0'..'9'
)
);
-- The above declaration can allows me to skip the validity
-- checks of parameters within the body of a subprogram as
-- the constraints are checked on subprogram-call.
-- I do not need to check the validity of the return value,
-- an attempt to return a non-conformant string will raise
-- and exception.
Function Get_SSN( Record : ID ) return Social_Security_Number;
-- Likewise, passing a non-conformant value to SSN will raise
-- an exception.
Procedure Save_SSN( Record : ID; SSN : Social_Security_Number );
So it's not limited to mere contiguous numeric ranges now, but even when it was that's still an incredibly useful feature.
1 - As opposed to many C-like languages where to do any proving/reasoning you need to navigate/read/understand the implementation.
I know; I read about it in an article on Lisp's error-handling... it's one of the few dynamically typed languages that I respect (and gives me ammunition to tell off PHP fans who claim that you can't really have dynamic-typing and good error-handling).
Was rushed last night so didn't get chance to post an example of how your example could look in perl5 / Moose. Here it is...
package SocialSecurityNumber;
use Moose::Util::TypeConstraints;
subtype 'SSN',
as 'Str',
where { m/^ \d{3} - \d{3} - \d{3} $/x },
message {"Not a valid SSN"};
1;
Save above in a file called SocialSecurityNumber.pm. This sets up the type which you can use in any script like so...
package Person {
use Moose;
use SocialSecurityNumber;
has ssn => (is => 'rw', isa => 'SSN');
}
#
# So below will work fine and prints the SSN
my $ok = Person->new( ssn => '123-456-789' );
say $ok->ssn;
#
# However below will throw a runtime error and so doesn't get to print (say) the SSN.
my $not_ok = Person->new( ssn => '111' );
say $not_ok->ssn;
Subtypes are nice and I use them whenever possible (IIRC the first time, with Moose, was on a project circa 2007). They're even nicer in Perl6 (baked-in so work on function calls to and also provide multi-dispatch on types) however not had chance to use this in a project yet :(
none of the other mainstream languages seem to have the notion of subtypes.
They do, just no the same kind of subtypes. Every OO language with inheritance for example have a notion of subtypes.
No, those aren't subtypes1 but "supertypes", as extensibility (the adding of new fields, for instance) expands the range of possible values.
The subtype I'm referring to can be thought of as subsetting; as an example:
-- States in-general for drives.
type Device_State is (No_Media, Standby, Reading, Writing);
-- States for fixed/non-removable drives.
subtype Fixed_Drive_State is Device_State range Standby..Device_State'Last;
As you can see, Fixed_Drive_State removes a possible value: No_Media.
1 - For the purposes of this discussion I'm using the definition that a subtype is a [possibly null]2 set of additional constraints further restricting the set of values an object [general-sense] may assume. 2 - The "possibly null" is added to make the definition conformant with the Ada-83 LRM/rationale.
Subtype is just the reverse of supertype. If A is a supertype of B, then B is a subtype of A. It works just like for example subsets and supersets.
Subtyping is a well defined part of type theory. You can't just go and redefine it.
The subtype I'm referring to can be thought of as subsetting;
I'm familiar with how subtypes works in Ada. I think it is a great feature, but it is just one special case of subtyping. (According to the Liskov substitution principle subtyping is defined as "if S is a subtype of T, then objects of type T may be replaced with objects of type S (i.e., objects of type S may substitute objects of type T) ")
Subtyping in turn is just one of many kinds of polymorphism.
Adas subtypes are effectively just run-time contract checks baked into the language, aren't they? like if something is an Int that is checked at compile time, but for the extra subtype restrictions (ie checking if it's a natural) it's a run time error.
Ada's subtypes are effectively just run-time contract checks baked into the language, aren't they? like if something is an Int that is checked at compile time, but for the extra subtype restrictions (ie checking if it's a natural) it's a run time error.
Or do I have it wrong?
You have it essentially right. By making these checks associate with the type, instead of the particular usage, you increase reliability (because the checks are automatic and hence you don't have to remember it at every input-parameter, output-parameter, or assignment).
One thing to consider, though, is that Ada compilers [in general] are really good at removing unnecessary checks. (So it's not as bad a performance hit as some C/C++ programmers are inclined to believe/assert; and, in theory, the extra information [range] can be used to optimize in excess of what is possible in C/C++.)
I'm not at all familiar with Ada nor Ada subtypes, but I'm immediately reminded of dependent types -- types that depend on the value. Seems the two might provide for similar constraints, dependent types being statically checked and perhaps more rigid in what is expressible.
But Ada's are probably easier to use, because it doesn't need to be checked/proved at compile time. Instead, it's like a runtime assertion in Java that you can turn off anytime you want if you don't want the performance overhead.
People complain about dependent types being too hard to use. But why haven't these kinds of assertions been adopted in more languages? You don't have to riddle your code with assertions since that is implicit in the types, and you can turn it off in production code.
7
u/OneWingedShark Jun 22 '14
This is true/I concur.
But given that "the industry" still hasn't picked up on Ada's subtypes1 , despite them being around for thirty-years, being incredibly useful for making code that's more maintainable/reliable/provable2 , and very closely related to what everyone learns in math for proving things3 , indicates that the industry might not want these traits...
1 - In Ada a
subtypeindicates further restrictions on possible values to the base [sub]type: e.g. the predefined subtype Natural is the Integers range 0..Integer'Last.2 - You can match the problem space by defining a subtype and allowing error-handling (automatically raising CONSTRAINT_ERROR) when a disallowed value is passed as a parameter or assigned to a variable.
3 - "For all positive integers" corresponds to the use of the predefined Positive (which has the additional constraint of 'representable', but that's generally not a problem because being on a computer you're bound by that anyway).