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. |
|||
00:45
Rimnal joined
00:48
Ebudae left
00:58
teatime left
00:59
teatime joined
02:17
teatwo joined
02:20
teatime left
03:57
Rimnal left
04:06
Heptite joined
04:08
Ebudae joined
04:12
Heptite left
04:17
Ebudae left
04:56
deoac left
06:59
kjp left
07:00
CIAvash left,
uzl[m] left
07:02
kjp joined
08:41
uzl[m] joined
12:42
CIAvash joined
12:56
habere-et-disper joined
|
|||
habere-et-disper | m: say (1..*).max | 12:57 | |
camelia | Inf | ||
habere-et-disper | m: Int.max | ||
camelia | ( no output ) | ||
habere-et-disper | m: say Int.max | ||
camelia | -Inf | ||
habere-et-disper | Why is that? Is this another edge case thing? | 12:58 | |
Shouldn't `Int.max` be `Inf` too? | |||
moritz | m: say Inf ~~ Int | 12:59 | |
camelia | False | ||
moritz | that's the problem | 13:00 | |
habere-et-disper | Is it trying to say it's undefined ? `Inf` aside, how can it be negative ? | 13:07 | |
m: say UInt.max.sign | |||
camelia | -1 | ||
13:16
Heptite joined
13:37
habere-et-disper left
|
|||
Nemokosch | I'm not sure if this was you but I have seen this mistake before. Int is not a set of values, neither is UInt, for that matter | 14:12 | |
14:17
tea3po joined
14:18
tea3po left,
tea3po joined
|
|||
in this context, Int, or UInt, is simply a placeholder value for something missing, hence it's immediately disqualified from the comparison, and you are basically asking the maximum of nothing | 14:19 | ||
14:20
tea3po left,
tea3po joined,
teatwo left
|
|||
so it is indeed "another edge case thing" and probably it would be better if it didn't execute in the first place | 14:21 | ||
also, it could be a problem that Inf is not an Int but here that doesn't matter; Num.max is also -Inf for the same reason | 14:22 | ||
I have already mentioned the problems of a mere Num value being used as a type-agnostic wildcard; this is a different thing | 14:24 | ||
gfldex | m: Int.^mro.say; | 15:35 | |
camelia | ((Int) (Cool) (Any) (Mu)) | ||
16:21
deoac joined
16:55
deoac left
|
|||
librasteve | en.wikipedia.org/wiki/IEEE_754#Infinities | 20:19 | |
your handy FPU has a standard way of doing +/-Inf ... this is the raku way to leverage that | 20:20 | ||
the idea afaik is that as you get beyond the range of Rats then the efficient way for your machine to handle bigger numbers is Nums so there is graceful degradation of precision, but not of accuracy | 20:22 | ||
then, if you run out of Nums you get to Inf | 20:23 | ||
m: say Inf ~~ Real | 20:25 | ||
Raku eval | True | ||
Nemokosch | Yes, Inf has a clear meaning in floating point context; however, we have it here in untyped context, conceptually | 20:31 | |
so there is this cross-proliferation | |||
the end result is that you can have an array of type X (say, integers) such that the maximum or minimum value is not of type X but a completely arbitrary type (floating point number) | 20:34 | ||
librasteve | raku is brilliant in "untyped" concept ... since it can seamlessly handle ops that combine Int, Rat, Num AND has a type model with proper inheritance so that | ||
m: say Int ~~ Real; say Num ~~ Real; | 20:35 | ||
Raku eval | True True | ||
librasteve | for example | ||
m: say Int ~~ Real | |||
Raku eval | True | ||
librasteve | m: say Num ~~ Real; | 20:36 | |
Raku eval | True | ||
librasteve | and so on | ||
m: (1, 1.1, 1e1).are | 20:37 | ||
Raku eval | |||
librasteve | m: say (1, 1.1, 1e1).are | ||
Raku eval | (Real) | ||
Nemokosch | okay but this has nothing to say about the sanity of a maximum of an array degrading into a different type | 20:38 | |
in before yes, this has caused me actual problems in code | |||
librasteve | m: say [+] (); say [+] (2); say [+] (2,3); | 20:39 | |
Raku eval | 0 2 5 | ||
librasteve | so, as you have said, the identity operation of + is 0 | 20:40 | |
m: say [] (); say [] (2); say [*] (2,3); | |||
Raku eval | 1 2 6 | ||
librasteve | and the identity operation of * is 1 | ||
m: say [max] (); say [max] (2); say [max] (2,3); | 20:41 | ||
Raku eval | -Inf 2 3 | ||
lakmatiol | JS also does the maximum of 0 is -Inf thing, and it is pretty much never as ergonomic as a language designer would like it to be | ||
Nemokosch | that's because * and + are designed to be operators unambiguous and exclusive to usual math | ||
librasteve | so I like that the identity of min/max has the same approach | 20:42 | |
Nemokosch | which is something that I have advocated for every now and then, i.e "don't ever overload these operators please" because of the broken assumptions | ||
however, minimum and maximum are conceptually overloaded for different types of data | 20:43 | ||
librasteve | huh? | ||
Nemokosch | I got curious and I checked this. Is there a general purpose utility for min/max, or just Math.min? | 20:44 | |
lakmatiol | just the Math.min and Math.max afaik, but they are variadic | ||
Nemokosch | for Math.min, I'd say it might actually be sane | ||
In JS "Math" usually implies floating point context | 20:45 | ||
lakmatiol | even among floats, it is an error more often than it is a desirable default | ||
Nemokosch | it wouldn't make sense to ask Math.min about the strings, right? | ||
lakmatiol | ye, it is less bad in JS | ||
Nemokosch | Well I can't see why it wouldn't be a desirable default but perhaps I'll have to take your word for it | 20:46 | |
librasteve | so imo, raku is saying "if you don't care about types, then you can min/max anything Real in a List, but WATCH IT if your List is empty" | ||
Nemokosch | min/max even works (and it is supposed to work!) on strings, and any types with a given cmp ordering | 20:47 | |
librasteve | you can't ask raku to give you min/max of Strings | ||
Nemokosch | you can | ||
you absolutely can | |||
librasteve | since it uses a <=> not a cmp and that coerces the Str to its .elems | 20:48 | |
lakmatiol | m: <a b c b a>.max andthen .say # this is what Nemokosch means | ||
Raku eval | c | ||
Nemokosch | it doesn't coerce into anything, it uses cmp actually | 20:49 | |
lakmatiol | julia julia> minimum([]) ERROR: ArgumentError: reducing over an empty collection is not allowed This just seems like objectively superior behaviour to me, especially in raku where you have excellent facilities for dealing for various kinds of failures. | ||
librasteve | oh - my bad - thanks for educating me | ||
all the same, I think that raku is not just another julia for this kind of things | 20:51 | ||
Nemokosch | github.com/rakudo/rakudo/blob/704a....pm6#L1584 by the way | 20:52 | |
yes yes, I know, not beginner-friendly enough | |||
but ultimately it's a common interest to get people into Rakudo anyway, whatever I think about that | |||
librasteve | making raku behave like julia is not going to get people into it ;-) | 20:53 | |
lakmatiol | though to be fair, -Inf does compare less than to most things | ||
m: max ().max, <a b c b a>.max andthen .say | 20:54 | ||
Raku eval | c | ||
librasteve | the point is that raku is designed to work very well when you are in untyped mode (as well as in typed) | ||
Nemokosch | Comparing -Inf and 'honeybee' makes no sense tho | ||
librasteve | why not? | ||
lakmatiol | well, the solution I would accept of raku would be a special polymorphic "less than everything else" object | ||
which could very well -Inf | |||
which doesn't sound right | |||
Nemokosch | and by "makes no sense", I mean it's actively harmful to get a floating point number from some number of strings | ||
lakmatiol | the more interesting issue is how do I do a max with a default | 20:55 | |
Nemokosch | especially that Raku will then be happy to use -Inf the floating point number through a Stringy interface | ||
lakmatiol | raku > ().max // 5 -Inf doesn't work | ||
librasteve | m: say ( ().max , "honeybee").max | ||
Raku eval | honeybee | ||
librasteve | ^^ look no type error ... just works | 20:56 | |
Nemokosch | so afterwards you will have no way to even tell why you got the string "-Inf", unless you planned for this case a priori | ||
lakmatiol | there is no "use the rhs if the lhs is -Inf" operator, since -Inf is not actually a :U | ||
Nemokosch | it is DEFINITE, it is defined and I think it is even truthy | ||
m: Inf.so.say | 20:57 | ||
Raku eval | True | ||
Nemokosch | I don't think you can recover if you did call max on an empty (or empty-ish, only undefined values) Iterable | 20:58 | |
lakmatiol | you can know to check for -Inf, that's about it | ||
Nemokosch | you actually need to implement a saner default every time this comes up as a possible problem | ||
lakmatiol | IMO something like a Least:U with the same comparison semantics would be better | 20:59 | |
then I can // it, I can't ~ it, etc. | |||
Nemokosch | well, there is that issue I opened a while back | ||
librasteve | recover from what - it doesn't throw an error when you compose it (see my example) ... maybe I'm missing a concrete example here | 21:01 | |
lakmatiol | something like perl my @names = ...; my $example = @names.min; say "The first name alphabetically is $example" | 21:02 | |
librasteve | I think that the raku way to handle anything that is an operation is that in general it will never revert to a type object | 21:03 | |
lakmatiol | this does not behave usefully with -Inf | ||
Nemokosch | something else: | 21:04 | |
cdn.discordapp.com/attachments/768.../image.png | |||
lakmatiol | oh yeah, if you want the type checking, this won't type erorr, huh | 21:05 | |
well that's awful | |||
Nemokosch | and you know, frankly, one shouldn't need to argue about type sanity | ||
I'm definitely not a static typing freak but it's just damn obvious that a type system is only useful if it's sound | 21:06 | ||
librasteve | raku type checking is often dynamic --- don't see an issue with that in particular | 21:07 | |
Nemokosch | you should at least see an issue with an array of strings producing a floating point maximum | 21:08 | |
lakmatiol | ye, it depends on if raku wants to have an optional type system, or something to the clojure spec kinda thing where it's runtime assertions | ||
Nemokosch | if you don't see a problem with that, I don't think there is any point in discussing this whole thing | ||
librasteve | in the input example you are saying "I will accept any input, then try to coerce the result of an operation to Str" ... which I guess you get the Str "-Inf" if you enter an empty Str | ||
Nemokosch | it shouldn't ever hit a floating point value | 21:10 | |
librasteve | raku is a world where you can have typed or untyped "contexts", when in untyped, I see no issue with mixing types | 21:11 | |
otherwise you have Rust | |||
lakmatiol | there is no typed variant of min | ||
min is simply always unsound | 21:12 | ||
librasteve | min is an operation | ||
not a variable | |||
lakmatiol | functions also have types | ||
Nemokosch | like how to put it... having Inf come out of any sort of non-numeric list is equally bad as having 42 come out | 21:13 | |
they are both arbitrary values one needs to write further checks for, wherever they happen | |||
and if you don't remember to write those checks, nonsense will come out, implicitly | |||
it's guaranteed to be nonsense by the sheer nature of particular types not having any concept of Infinity, let alone a numeric one | 21:14 | ||
librasteve | I am not sure that raku maintains a concept of sound operations where the type of the result is aligned to the type of the argument | 21:15 | |
lakmatiol | that's a bad thing if you want a useful type system though | ||
if you see the the type specification system as a way to do runtime assertions on types which can occasionally figure out an error at compile time, it is fine | 21:16 | ||
librasteve | yes and no - it would be a killer with strong / static typing, I agree | ||
so raku "gradual" typing is not everyone's cup of tea since a lot is left to runtime | |||
Nemokosch | this is not just about the type system being static | ||
librasteve | but I for one much, much prefer it to eg Rust | 21:17 | |
lakmatiol | gradual typing generally implies that the parts you do type are typed soundly. | ||
Nemokosch | types themselves are an abstraction | ||
lakmatiol | which is just... not true in raku | ||
but it works fine in e.g. python | |||
librasteve | well, so yes there is a interface between untyped and typed worlds | ||
Nemokosch | if you have a list that you initialize a certain way, you can semantically describe the type of your data, that's how you can think of dynamically typed stuff as well | 21:18 | |
librasteve | and in raku this is gated with runtime assertions | ||
lakmatiol | well, yes, that's what I said, if you want to be in a typed world, you can never use min or max | ||
Nemokosch | now if you can semantically expect your list to contain a certain number of strings, and then you cannot expect the minimum of them to either be a string, or at least warn about a special case going on, that's definitely a problem | 21:19 | |
whether you use type annotations or not | |||
I for example did not, yet it was a problem, because of the useless semantics of the outcome | |||
a type error is really not the worst thing that can happen | 21:20 | ||
having a nonsensical string come out 10 operations later, is way worse | |||
so granted, this sort of soundness is strictly necessary for proper static typing - but it is also universally beneficial without | 21:21 | ||
librasteve | would you outlaw doing min/max unless all types in list the same? | 21:22 | |
lakmatiol | if you are writing type hinted code, you will likely have that property upheld anyway, so sure | 21:23 | |
Nemokosch | let's say the tradeoff explicitly | ||
the behavior that shoves numeric Inf values onto every use case is beneficial for the use cases where you do indeed mean to compare numbers, and floating point numbers in particular | 21:24 | ||
librasteve | m: my @names = []; my Str $example = @names.min; | ||
Raku eval | Exit code: 1 Type check failed in assignment to $example; expected Str but got Num (Inf) in block <unit> at main.raku line 1 | ||
lakmatiol | it is useful for any case where you want to do further comparisons on the result, to be fair | 21:25 | |
Nemokosch | it saves you, say, one condition | ||
one condition that you would know about rather clearly, though | |||
librasteve | ok, so we agree that min/max in untyped context should be able to handle a mixed List - phew! | 21:26 | |
lakmatiol | honestly, the example for static typing may as well just be my Int $i = <a b c>.max, which also crashes at runtime | ||
Nemokosch | on the other hand, it is detrimental in any other cases, in a way that you need to explicitly learn that although ().min/max does give you a value, it will be a useless value that you need to guard against | ||
lakmatiol | I did kind of forget that the raku type checking is more cosmetic than useful. | 21:27 | |
Nemokosch | if min/max was strictly about numeric comparison, Inf - preferably as an abstract Real value, not a Num - could do well | 21:28 | |
librasteve | so, then the question is should min/max be able to mix numbers and strings given it uses cmp semantics, which boils down to should cmp be able to take a number and a string | ||
lakmatiol | that's not quite the discussion | ||
Nemokosch | well, that could also be a discussion š | 21:29 | |
there are many things to get wrong here, to be honest | |||
have you heard of IntStr? | |||
lakmatiol | the question is: should a function introduce a whole separate type of value which at no point anyone actually cared about or even instantiated? | 21:30 | |
librasteve | m: my Str @as="a","b"; my Int $a = @as.max; say $a; | ||
Raku eval | Exit code: 1 Type check failed in assignment to $a; expected Int but got Str ("b") in block <unit> at main.raku line 1 | ||
lakmatiol | when it's a:U, it's a thing that raku has tools for dealing things, same with a failure of sorts. But a plain ol' float requires a special check entirely | 21:31 | |
Nemokosch | I'm not sure that's really the question | ||
or maybe I just don't get you, lol | 21:32 | ||
lakmatiol | honestly, I am just not a fan of sentinel values | ||
should've kept that mess in C where it belongs | |||
would this pass code review? perl my $el = @xs.max fail "no elements" if $el ~~ Num | 21:33 | ||
Nemokosch | lmao | ||
librasteve | well lets backtrack from Least:U ... your suggestion .... I have said that this concept should be defined ... so I don't think it is unreasonable in an untyped context to pick up -Inf since that is definitively a standard way to say "the most negative thing this machine can represent" | ||
Nemokosch | no, because you forgot the semicolon! | ||
lakmatiol | truee | 21:34 | |
librasteve | lol | ||
I always forget the say | |||
Nemokosch | I think even this phrasing is presumptuous - a string is not positive or negative | 21:35 | |
neither is business logic object of any sort | |||
+ a | |||
lakmatiol | the smallest thing being -Inf is... cute, in the same way pony not having operator precedence is cute | 21:36 | |
librasteve | look, I would support some defined "lowest possible Str / highest possible Str" when doing a leg | ||
Nemokosch | not having operator precedence is not just cute but also simple | ||
librasteve | as a new raku build in class | 21:37 | |
Nemokosch | something not to neglect | ||
librasteve | yeah - but if we are in Real children territory it can fully participate in subsequent math and do a sensible thing | 21:39 | |
m: say Inf + 1 | |||
Raku eval | Inf | ||
lakmatiol | min is not a numeric operator though | 21:40 | |
it works on anything you can compare, and so it should behave like it. | |||
and while -Inf does indeed behave like the smallest thing, it also behaves like a lot of other things, only some of which make sense in a given context | 21:41 | ||
librasteve | well I am basing on cmp being [<=>|leg] and that if all are Str then leg would apply | ||
but this idea of LowestPossibleString is also bad because you can't just use 'A' if you want to follow the identity pattern | 21:42 | ||
Nemokosch | whether we like sentinel values or not, there are clear differences | 21:45 | |
what purpose they are defined for, or how easy it is to mistake them for valid values | 21:46 | ||
librasteve | maybe '' (empty Str) is a good candidate for LowestPossibleStr | ||
but I can't fathom what to do for HighestPossibleStr | 21:47 | ||
Nemokosch | although I also don't vehemently oppose the idea that "the smallest something in the universe" just shouldn't be a valid query, and hence should throw some way | ||
librasteve | so - I dare to say that we both think that raku should warn if you min/max an empty List | 21:48 | |
but that no one has a better idea than +/-Inf when it comes to the return value even of a Str array | 21:49 | ||
Nemokosch | I think even dedicated "universal maximum/minimum" values could be better, or Nil, or Empty, or some sort of Failure | 21:50 | |
librasteve | OK - how about "no change to numbers, but if all .are ~~ Str then return Nil from ().min and ().max and warn 'taking the min/max of an Empty List' | 21:52 | |
" | 21:53 | ||
Nemokosch | how do you know about an empty list how it came to be? | ||
I mean, as the core language API | |||
librasteve | oh rats - getting late | 21:54 | |
that's why I not a language designer! | 21:55 | ||
must go ... | 21:56 | ||
Nemokosch | and that's why I gave up on trying to bring certain design issues into attention |