|
Pablo Aliskevicius wrote: I remember reading an article (which I can't find) that used this to implement classes that allow you to do the following:
Yes, I was just reading about that. There's a CP article that also does some pre-processing for C# that allows units of measure to be specified, very similar to F#.
And yes, that's one piece of the puzzle I'm working on.
Marc
|
|
|
|
|
Yes. F# supports units of measurement[^].
For example, you can do this:
[<Measure>] type years
let myAge = 32<years>
The downside this is strictly language support, and not runtime support. Units are lost at runtime, but it's still pretty handy.
|
|
|
|
|
Phil Martin wrote: Units are lost at runtime
Which is unfortunate because I'd possibly like to be able to reflect on the unit of measure. But it's an interesting avenue to explore. Thanks!
Marc
|
|
|
|
|
Yeah, it is unfortunate.
In the engineering applications I write, I've created a ScalaryQuantity and a VectorQuantity class. They are just the usual numeric structures which support all the normal arithmetic, but supports keeping track of units, and converting units when necessary.
There's a big run time overhead involved, but for me it is worth it because it has helped me catch many errors far earlier in the process of developing new calculations.
|
|
|
|
|
Annoyingly, int is a sealed type in C#, or all you would have to do is provide the implicit cast operators:
class AgeInYears : int
{
public static implicit operator AgeInYears(int i)
{
return (AgeInYears)i;
}
public static implicit operator int(AgeInYears a)
{
return (int)a;
}
} But...what is an age plus an age? It's not really anything useful if you think about it. What you should be thinking of here is an Age plus a Timespan equals a DateTime, but then an Age can't really be assigned an integer value unless it already has a Datetime component - perhaps it is relative to the time at which the Age object is instantiated?
And don't forget that an Age is not a constant value: it will vary as the application runs...
|
|
|
|
|
OriginalGriff wrote: Annoyingly, int is a sealed type in C#, or all you would have to do is provide the implicit cast operators:
Exactly!
OriginalGriff wrote: And don't forget that an Age is not a constant value: it will vary as the application runs...
I know. It was a contrived example.
OriginalGriff wrote: But...what is an age plus an age?
Yeah, this stuff gets one to really think about the meaning of things.
Marc
|
|
|
|
|
You could argue that AgeInYears should be implemented something like this:
class AgeInYears
{
public AgeInYears(DateTime birthdate)
{
_Birthdate = birthdate;
}
private DateTime _Birthdate;
public int Years
{
get
{
return (DataTime.Now - _Birthdate).Days / 365;
}
}
}
Software Zen: delete this;
|
|
|
|
|
|
Yeah, this reeked of prior art but I couldn't be arsed to go looking just to comment on a casual question.
Software Zen: delete this;
|
|
|
|
|
A long time ago, at a defense contractor far, far defunct...
The Ada programming language provided a semblance of semantic typing. You could create an 'Age' type that was a subtype of integer. I'm sure the computer scientists would scoff at Ada's limitations, but it does somewhat fit the bill. I don't know the modern language definition (I used it back in the 80's), so it might be more capable now.
Software Zen: delete this;
|
|
|
|
|
Gary Wheeler wrote: The Ada programming language provided a semblance of semantic typing.
Ah, it does indeed. I've been reading the Ada type stuff - very slick. It's a pity these constructs aren't in other languages. I wonder why not - it seems like it would really help bullet proof code. Then again, like anything else, I bet it can be horribly abused as well.
Marc
|
|
|
|
|
Stroustrup has written somewhere on implementing SI measures in C++ using user-defined literals and simple classes. That would work well, but should really be part of the standard library.
"If you don't fail at least 90 percent of the time, you're not aiming high enough."
Alan Kay.
|
|
|
|
|
Rob Grainger wrote: "If you don't fail at least 90 percent of the time, you're not aiming high enough."
Well. Try this with your employer!
(it's a joke)
|
|
|
|
|
I still feel that would be an improvement on some of the folk whose footsteps I'm following (see various entries in The Wierd and The Wonderful) - at least I'd succeed 10% of the time
"If you don't fail at least 90 percent of the time, you're not aiming high enough."
Alan Kay.
|
|
|
|
|
The central goal behind Ada was to make it difficult to make common programming mistakes, and to have the compiler enforce as many conditions for correctness as possible. The language design succeeded in a lot of respects, but it made it difficult to write code in the language. It was very frustrating learning how to appease the compiler.
In my case, we were beta testers for one of the first VAX/VMS Ada compilers. It was difficult to tell the difference between genuine flaws in our source and possible compiler bugs.
Software Zen: delete this;
|
|
|
|
|
Well
This c++ 11 trick is very powerfull, really!
I prefer to do all the hard work from scratch (ok, have R #).
I believe that this threshold "semantic" is outside the domain of a programming language (commonly, it is a "system domain" concept), because it is difficult to predict the particularities of a user-defined (conversion, comparison, integrity, serialization, etc.).
|
|
|
|
|
greydmar wrote: I believe that this threshold "semantic" is outside the domain of a programming language (commonly, it is a "system domain" concept),
Yes, that's what makes it interesting to look at.
Marc
|
|
|
|
|
Well, lets consider the following task:
Imagine the same issue when your development includes (for example) some SQL tables with some (or a lot of) semantic fields (unit of measure, complex number, currency, etc.) and you decides (of course, you are a "programer"!! ) performs some calculations using SQL dialects (T-SQL; stored procedures, udf, package, PL/SQL)...
So, it's a kind of art do ordnary operations (conversion, arithmetics) without "semantic types", no?
|
|
|
|
|
"which completely loses the concept that 51 is an age (in years)."
so write
int ageInYears = 51;
There are many ways to do it in c# (pass the int value in constructor, make an implicit cast operator, ...), but imho the best way is sticking with plain int, as that what "age in years" exactly is.
|
|
|
|
|
CHILL (CCITT HIgh Level Language - CCITT was the old name of ITU-T) probably has the best developed strict type system of any industrial language. One of the features was the distinction between a SYNMODE and NEWMODE definitions (MODE is the CHILL term for type/class): A SYNMODE defines restrictions (like value subranges) or aggregates (like arrays) of existing types, but (within the restrictions) fully compatible with the base mode. A NEWMODE is similar, but defines an incompatible mode. So if you make new integer modes AppleCount and OrangeCount using SYNMODE, you can add apples and oranges. If you define dem using NEWMODE, the compiler won't allow you to add apples and oranges without an explicit cast.
CHILL was developed to be the ITU standard for programming telephone switches, but the language design is just as general as, say, c or java. It never made any success in non-telephone environments (and even there it never took more than about half of the market), which is a pity: CHILL is one of the most thoroughly well-designed languages there is. But then again: The marketplace isn't known for always selecting the best designs... (I won't give c as an exapmple of that, that could hurt some people's feelings).
|
|
|
|
|
Member 7989122 wrote: CHILL (CCITT HIgh Level Language - CCITT was the old name of ITU-T)
Interesting! Your description of restrictions, aggregates, base modes, and incompatibility reminds me of what I was reading about Ada.
Marc
|
|
|
|
|
Marc has already pointed to Smalltalk, someone else pointed out C++, I'd like to add Haskell to the mix.
Getting type safety would be easy:
newtype meter m = meter m deriving (Show, Num, Eq, Ord)
newtype foot f = foot f deriving (Show, Num, Eq, Ord)
-- Usage (ghci> is an iteractive prompt)
ghci> let m1 = meter 5
ghci> let m2 = meter 10
ghci> let m3 = m1 + m2
ghci> m3
meter 15
ghci> let f1 = foot 3
ghci> let f2 = foot 4
ghci> let f3 = f1 + f2
ghci> f3
foot 7
ghci> let e1 = f1 + m1 -- Won't Work - produces error of types mismatching
With more work, it can be extended to add support for conversions, magnitudes (nm, mm, m, km, ...).
Indeed someone has already done all this work for us: Dimensional[^] and Dimensional using type families[^].
All available at the standard repository Hackage[^].
"If you don't fail at least 90 percent of the time, you're not aiming high enough."
Alan Kay.
|
|
|
|
|
Fascinating - I was just reading the Python example below and then looked at your Haskell example. Thanks! Also, I appreciate the links.
Marc
|
|
|
|
|
Don't know if your familiar with Haskell at all, but its a wonderful thing. One day I'll undwerstand it well enough to use on a real world project.
The example given is relatively simple. I particularly like being able to derive properties (type classes) like Ord (a bit like comparable), Num (numeric), Eq (equatable) and Show (convertible to string) automatically for many types.
Be aware that "type classes" are not classes in an OO sense at all. More like interfaces. Even more like "concepts" in C++ (when they finally make it into the language).
I think I've learned more from learning Haskell than any language since I learned Smalltalk.
"If you don't fail at least 90 percent of the time, you're not aiming high enough."
Alan Kay.
|
|
|
|
|
Rob Grainger wrote: Don't know if your familiar with Haskell at all, but its a wonderful thing.
Nope, but I've been reading up on it since your previous post!
Rob Grainger wrote: I think I've learned more from learning Haskell than any language since I learned Smalltalk.
Interestingly, I learned more about the principles of programming from this book[^] than I ever have from any actual comp-sci book. I kid you not - biology and programming have a lot in common.
Marc
|
|
|
|