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
|