I appreciate the value of immutable data structures, and really like that Raku has many built in. I particularly like that the compiler/typechecker will enforce immutability for me – I might have an off day or get careless about something, but the compiler never will.
Or at least, that's what I thought.
I was very surprised to see, however, that the following code runs without a peep from the typechecker:
my Map $m = Hash.new('key', 'value');
say $m.WHAT # OUTPUT: «(Hash)»
After consulting the docs, I see that Map is a parent class for Hash (and thus Hash.isa('Map') returns True. So I understand how (on a mechanical level) that typechecks successfully. But I'm left with two questions: first why does the inheritance work like that and, second, what can I do about it if I really want the typechecker to guarantee that my immutable variables stay that way.
On the "why" question — what's different about Maps that they're built like this? None of Raku's other immutable types are: Set.isa('SetHash'), Mix.isa('MixHash'), Bag.isa('BagHash'), Blob.isa('Buf'), and (if it counts) List.isa('Array') all return False. [Edit: as jjmerelo points out below, I reversed all of these. I should have said SetHash.isa('Set'), MixHash.isa('Mix'), BagHash.isa('Bag') and Buf.isa('Blob') all return False. Interestingly, Array.isa('List') returns True, which lends some support to Elizabeth Mattijsen's statement that this is a historical oversight – Lists and Maps are definitely more fundamental data types than most of the other immutable types.]
What's different about Maps and Hashes that they have this behavior?
On the more practical question, is there anything I can do to get the typechecker to help me out more here? I know that, in this specific case, I can write something like
my Map $m where { .WHAT === Map } = Hash.new('key', 0); # Throws the error I wanted
Or even
subset MapForRealThisTime of Map where { .WHAT === Map }
Are those really the best alternatives? They both feel a bit clunky (and the where block could potentially have a runtime cost?) but maybe that's the best approach?
More generally, what I'd really like is a way to typecheck in strict mode, so to speak. If I explicitly declare the type of a variable, I'd really like the compiler to guarantee that the variable has that exact type – not some other type that happens to have that type as a parent. Is there any more general approach I can take, or am I just asking for a level of strictness that Raku isn't going to provide?
