This channel is intended for people just starting with the Raku Programming Language (raku.org). Logs are available at irclogs.raku.org/raku-beginner/live.html
Set by lizmat on 8 June 2022.
nemokosch yeah, I don't think that would work 00:34
there are two possible interpretations of a coercion. What does "coerce to T" mean in the context of a type that is derived from T? 00:38
According to one interpretation (I'd say this is the common interpretation in the OO world, in accordance with the Liskov Substitution Principle), it should be an identity, since the source type itself is a kind of T
According to another interpretation (this is the more common interpretation in Raku), a restricted instance should be retrieved; one that looks like T dynamically as well, the additional data erased from it 00:40
while coercion methods work like this, coercive types actually work like the former way - "an IntStr is always an Int, so no coercion is necessary", says the resolution 00:42
the shock comes when you learn that the subroutine-looking cast also works like this, and unlike the method-looking cast 00:44
m: say 'An allomorph Int being the same as a bare Int is ', <95> === 95; say 'An allomorph Int being the same as its Int(...) coercion is ', Int(<95>) === <95>; say 'An allomorph Int being the same as its .Int coercion is ', <95>.Int === <95>; 00:46
Raku eval An allomorph Int being the same as a bare Int is False An allomorph Int being the same as its Int(...) coercion is True An allomorph Int being the same as its .Int coercion is False
nemokosch m: say 'IntStr.Int being the same as the underlying Int value is', <95>.Int === 95; 00:47
Raku eval IntStr.Int being the same as the underlying Int value isTrue
01:37 deoac joined 03:08 deoac left 04:12 razetime joined 05:28 siavash joined 05:34 siavash left 05:52 siavash joined 07:29 siavash left 08:38 lizmat_ left, lizmat joined 08:39 teatwo left, teatwo joined
librasteve crag-of-the-day crag '$x = <௪௨>, say "$x is Tamil for {+$x}." ' #௪௨ is Tamil for 42. 09:50
^ the allomorph is a great way to maintain the original Str definition alongside the value so that you can do Stringy and Numeric operations without losing the connection - this is neat for western languages where values may be normalized <2/20 10e3> 09:53
it is even more helpful where you are dealing with unicode digits 09:54
nemokosch You can achieve that by simply storing a string because Raku is a weakly typed language with very aggressive numeric coercion 09:55
librasteve but that will fail any type checking $x ~~ Int 09:59
nemokosch And that is a very good thing
That's a feature
librasteve it may be if this was Java 10:02
nemokosch a value that isn't identical to any of the integer literals is better to not come off as Int
librasteve you are confusing identity with type 10:03
nemokosch again, that should be a good thing, a feature
really, take the example that highlighted the whole topic this time, with <0x09>
what does it bring to the table that "it is an Int", if in no hash-alike data structure will it behave like an actual concrete integer? 10:04
because neither its identity, nor its string form matches any of the integers 10:05
so not only do you have to awkwardly coerce it into Int but its recognition as Int will actively get in the way! 10:06
10:06 ab5tract joined
you don't get warned about it if you use gradual types and it won't get coerced if you use a coercive type annotation 10:06
for all practical purposes, it wasn't more an Int than any Str would be that holds a numeric value, except its much harder to even extract the value 10:07
lakmatiol This is absolutely horrifying, I must say > my %h{Int} = <1> => 1, <0x1> => 2 {0x1 => 2, 1 => 1} 10:09
nemokosch and then you could write 1 => 3 as well and that would be a third entry 10:10
lakmatiol ah, good point 10:11
IG the conclusion is don't put allomorphs into hashes, and stick to postcircumfix <> as just being a syntax for accessing string keys
nemokosch maybe I should start adding disclaimers which topics bit me personally 10:12
the allomorph topic bit me twice: once with the hash-alikes (it was a set iirc), and once with the boolean check
because that's also quite the unfeature that you get a data type from prompt that is literally unsafe to perform a boolean check on 10:13
because the semantics(!) of the boolean check will depend on the input 10:15
lakmatiol > dd so prompt 000e15687 Bool::False Nil well, this is certainly interesting 10:16
nemokosch yes... an arbitrarily long input can generate a boolean False value, and I'm wondering: who wants this behavior at all, let alone as the default? 10:17
I'm still asking: do you have ideas what practical purpose allomorphs achieve? I could only think of one - exposing the OO interface of multiple types at once (the procedural interface is exposed by coercions) 10:20
lakmatiol they do pass both typechecks, which may be valuable if typechecking is a common thing to do, which it is seeing as the language has a bunch of syntax sugar to insert type checking sugar in many places. 10:22
nemokosch so far it seems that's actually the worst thing about it, though
that it claims to be something and fails to deliver the minimal promises of that type
lakmatiol well, the other option is that a string that looks like a number is only occasionally a number 10:23
nemokosch is this about the OO interface again?
by the way, your <1>, <0x01> example is a good one; identity is problematic even among other allomorphs 10:28
lakmatiol the simplest example I can come up with is my $a = prompt; my $b = prompt; dd ($a + $b); This works fine, + coerces the Str/IntStr prompt provides. Now I want to do a more complicated computation, so I make myself a helper function sub do-math(Int $a, Int $b) { $a * $a + $b + $b } my $a = prompt; my $b = prompt; dd do-math($a, $b); This now requires an allomorph to work. But well, you can just use 10:34
Int() instead. Even in a typecheck, I can just do "5345" ~~ Int() and have it work correctly. But I could see a case where you don't want coercion, but do want to pass IntStr
I am quite interested in an example where using Str and Int() doesn't do the exact same job an allomorph does
nemokosch (sidenote: currently, smartmatches to Type() seem to be broken)
not sure if I reported it yet, it has been like this for a surprisingly long time 10:35
lakmatiol it is possible, I ran one example and had it pass, so I didn't think on it further.
ah yeah, nvm
nemokosch m: say 'almafa' ~~ Int()
lakmatiol you need allomorphs
Raku eval True
lakmatiol it does make sense-ish that it behaves like this
its just not useful
nemokosch how does it make sense? 10:36
I mean, I'm fairly confident this is a bug but still curious
lakmatiol well, ~~ checks if the lhs can fit into the type on the rhs, e.g. this fails perl > (:1st) ~~ Int(Str) False
and well, a Str can bind to an Int(), it may just error out in the conversion 10:37
nemokosch huh 10:38
I can only hope that this idea is not prevalent, to be honest 😅
it seems very harmful
lakmatiol anyway, IG I have my answer, you need an allomorph to avoid the python-like py try: v = int(value) except ValueError: print('didn't pass an int') else: print('got an int', v) 10:39
nemokosch no you don't: coercion signatures actually work, apart from smartmatching
lakmatiol an IntStr lets you just ~~ Int to check if this random string you found fits into an Int 10:40
nemokosch and smartmatching also worked until like 3 years ago
lakmatiol well, if smartmatching actually did the failing the coercion means false thing, an allomorph would be less useful 10:41
nemokosch it used to, until, let me look it up
github.com/rakudo/rakudo/commit/f2...e8f9076d6e 10:42
until this commit
by the way, it's not uncommon for Raku to coerce failures into False in a bool context 10:43
so even along that line of reasoning, it should rather be False
m: say 'almafa' ~~ *.Int
Raku eval False
nemokosch dang
lakmatiol consider something like given $tok { when Int { return .Int } when * \in keywords {return keyword-cb{$_}($ctx)} default {return env{$_}} }here an allomorph $tok would work ince 10:44
but well, there are other ways to do it too
ones which don't involve breaking hash identity is confusing ways
raku doesn't have a numeric hash like python does
so IG expecting heterogenous numeric hash keys to do anything useful at all is foolish 10:45
nemokosch also, I think it's quite yucky to ever have to write when Int { return .Int }
why coerce it if it is an Int
lakmatiol well, that's the OO magic of not having a numeric hash - a subtype of Int is not an Int 10:46
nemokosch the paradoxical nature of this statement exposes some fundamental problems 10:47
lakmatiol not breaking the LSP with numeric subtypes is really really difficult
and lets not pretend raku doesn't shatter the LSP in other cases as well
nemokosch numeric subtypes probably weren't a good idea at all
but you are right, there is List and Array
lakmatiol having custom numeric types is a useful thing
nemokosch (and that also wasn't a good idea)
custom numeric types =/= extending existing numbers that pretend to be an existing type 10:48
lakmatiol doing custom numeric types without a numeric hash means you just run into the issues of heterogenous collections of numbers in vastly more cases than in python
nemokosch by the way, about "numeric hash" - it's an implementation detail what hashing "object hashes" use so it is for all intents and purposes a numeric hash 10:49
Python could choke exactly as much with numeric hashing if it derived from int
it's the identity that is the problem, not the hashing
lakmatiol python specifies a very specific way of hashing for numbers that are equal to an integer or a float 10:50
nemokosch it would be trivial to model a numeric hash as a string
lakmatiol which is why {1.0, 1, Fraction(1, 1), True, Decimal(1.0)} is a one element set in python, but (1,1.0).Set is a two element set in raku 10:51
nemokosch you may think whether that's a good feature or not, honestly 10:52
I don't think it's bad at all that 1 and 1.0 don't hash the same
it is a bad thing that something that is an Int, and is equal to 1, doesn't hash the same as 1 itself 10:53
1.0 never claimed to be the same
lakmatiol > True == 1 True > True ~~ Int True > (True, 1).Set Set(1 True) 10:54
nemokosch yes, that's also a problem
the weakest link might be True ~~ Int there 10:55
(enum ~~ Int in general)
it's really worrying that you can constructively generate contradictions in Raku
and again, it begs the question: why? what practical purpose does it serve? 10:58
lakmatiol well, raku as a language loves is a checks, even has a whole syntax sugar around them. I do not think it is surprising to find a lot of types are something just because they are "duck typing" compatible with the type. In python, thanks to the numeric tower, you don't run into this with ints, but you do run into this with mappings. If you have a public library function that takes an argument that is either a 11:06
dict or a string (e.g. 'fast' vs. { 'threshhold': 5, 'steps_per_t': 10 } ), 70% of the time you cannot use a Mapping there, since the library author just did isinstance(a, dict) and did not think on it further. Which is how you find dict subclasses in the wild that do not ever actually delegate to super and have their own internal storage, using the dict supertype just to pass type checks (these days there is
instancecheck, which does slightly help).
nemokosch I don't think making out a type to be something it isn't would work well in any typing system 11:08
especially for types that are predominantly used for their value, NOT their interface
lakmatiol well, the alternative is a significant part of the language just... not being polymorphic 11:09
which is very much not a perly thing to do 11:10
nemokosch for a complex stateful data structure, "I am what I do" may make sense, not for primitive values though
other than that, the weird thing is that it's not really in accordance with Raku's broader design to do things based on what you are 11:11
operations are provided by coercions
lakmatiol I would argue having nominative type checks is not inline with that design, and yet it is like half the language.
nemokosch what does that mean? 11:12
lakmatiol sub(Cls $x, Cls $y) is very much a case of I am Cls, and thus I can do things
and the language is full of this syntax for those kinds of checks
nemokosch but you better write it as Cls() if you want to support it for something else
lakmatiol so of course a lot of the language needs to do inheritance to work with those checks 11:13
nemokosch and out of the two approaches, this is way more prevalent for fundamental types
this is actually the big difference compared to Python
this is what "weak typing" is about
lakmatiol It is more noticable for Int, but there are real uses for polymorphic math functions, and thus raku needs to do all this weirdness with making int subtypes actually be a usable thing 11:14
Rust doesn't let you make an Int subtype
nemokosch you do not support an operation for Ints by becoming an Int yourself but by providing a way to coerce to Int
lakmatiol ye, but that is inadequate
nemokosch it is adequate for essential types 11:15
and for essential types, it shouldn't be mixed with anything else
lakmatiol 99% of the time, I need a custom a+b to provide my own type
not an Int that I then coerce back
nemokosch just really please don't overload + in Raku
don't overload an operator that is already dedicated to coercions 11:16
lakmatiol yeah, I am starting to see why the guy trying to do Rings in raku failed
which raises the question of what custom numeric type can I even make that will work.
nemokosch the question is really what even qualifies as a numeric type
a tensor surely isn't, for example 11:17
lakmatiol well, no, it isn't ordered
nemokosch Complex numbers also aren't 11:18
lizmat m: class A { method value() { 42 } }; multi sub infix:<+>(A:D $a, A:D $b) { $a.value + $b.value }; say A.new + A.new
nemokosch I have been thinking a lot about weak typing, to be honest
camelia 84
lizmat what's so difficult adding custom infix ops?
nemokosch weak typing is really good as long as you do operations on a couple of mutually known types 11:19
if you want to extend the type system, suddenly its strengths turn into weaknesses
lizmat example?
lakmatiol yeah, this may be a haskell situation where you should just give up on reusing the builtin stuff (yes, + is Num, but there aren't all that many custom Num things), and should write a new operator 11:20
nemokosch the premise of weak typing is that operations drive the data types
the premise of strong typing is that data types drive the operations
lizmat m: class A { method Numeric() { 42 } }; say A.new + A.new
camelia 84
nemokosch the question is what you do more often: define new operations on existing data types (good fit for weak typing) or define new data types with existing operations (good fit for strong typing) 11:21
lizmat m: say Date.today + 9 11:23
camelia 2023-09-11
nemokosch in Raku, if you expose a new operation that can deal with a well-known data type, everybody is happy because as long as they have meaningful coercions to the base type, their code will not only work with your operation but that code will have quite strong typing guarantees 11:24
lakmatiol most new types I write are not isomorphic to an existing type
nemokosch if nobody overloaded +, for example, whenever you encountered $a + $b, you would both know that $a and $b need to be coercable to Numeric, and that the return value will also be a Numeric 11:25
that is an underrated feat
however, if all you ever wanted is to add a brand new type and perform operations on that, you will have a harder time to provide an interface 11:26
lakmatiol I do feel like the quadratic nature of weak typing (N types have N*N coercions) makes it really hard to write a lot of code without just going back to the java-style nominative checks
nemokosch I think this is why eqv, before, after, cmp operators exist
weak typing is not extensible on types 11:27
ironically, coercions themselves depend on strong typing and polymorphism 11:28
strong typing can work without weak typing but not vice versa, at least in an OO environment
however, if we just started to overload + back and forth, we would get the worst of both world 11:29
s
depending on the nominative type, sometimes it would coerce, sometimes it wouldn't 11:30
the way I understand it, div and mod are also relics of "generic math operations" 11:32
except they "switched sides" at one point, long before the release
by the way, as much as weak typing is not extensible on types, of course strong typing is not extensible on operations, take a look at Python's magic methods 11:35
lakmatiol yeah, that's where things like typeclasses and whatever julia is doing come into play, which are extensible on both (with some caveats) 11:40
nemokosch I have weird feelings about weak typing overall... I think it's often misunderstood and more frowned upon than it actually deserves. After all, with a coercive op, $a op $b conveys way more information than it would with a generic op. But at the same time, I'm not sure it's genuinely worth it, due to the extensibility concerns 11:43
11:44 razetime left
I can see why somebody who mostly uses shell scripting would think that weak types are both efficient and robust 11:44
11:50 razetime joined 12:54 razetime left
yjh Hello.  Is the markdown flavor that modules.raku.org uses posted somewhere?  The README.md for the module I submitted displays fine on github and raku land but is mangled on modules.raku.org. 14:00
antononcube @yjh I understand your concern. I inclined to think that the Markdown on raku.land it is not mangled but rather not fully rendered. 14:08
14:12 razetime joined 14:54 jgaz joined
nemokosch yes, I think it's not an actual flavor 15:22
16:07 razetime left 16:36 jgaz left 20:20 tea3po joined 20:21 tea3po left, tea3po joined 20:22 tea3po left 20:23 tea3po joined, teatwo left 23:38 sivoais left, sivoais joined