The Raku Programming Language



  • @dfdub said in The Raku Programming Language:

    @Steve_The_Cynic said in The Raku Programming Language:

    @pie_flavor said in The Raku Programming Language:

    Some things that look like methods are actually keywords. This is fine. What's not fine is getting no error when you define a method with the same name, and getting no warning when you call it.

    class A {
        method WHAT { "ain't gonna happen" }
    };
     
    say A.new.WHAT;    # OUTPUT: «(A)␤» 
    say A.new."WHAT"() # OUTPUT: «ain't gonna happen␤» 
    

    Except that if they were really keywords, the precompiler would barf up jolly error messages when it encountered the definition of method WHAT. As it is, they are in a sort of half-and-half neither-one-thing-nor-another Limbo state.

    That's actually pretty normal. Many languages use "contextual keywords" that are valid identifiers, but have special meaning in some places.

    Which fits perfectly with my suggested concept of half-and-half neither-one-thing-nor-another Limbo state. I think so, anyway.



  • @Steve_The_Cynic said in The Raku Programming Language:

    @dfdub said in The Raku Programming Language:

    @Steve_The_Cynic said in The Raku Programming Language:

    @pie_flavor said in The Raku Programming Language:

    Some things that look like methods are actually keywords. This is fine. What's not fine is getting no error when you define a method with the same name, and getting no warning when you call it.

    class A {
        method WHAT { "ain't gonna happen" }
    };
     
    say A.new.WHAT;    # OUTPUT: «(A)␤» 
    say A.new."WHAT"() # OUTPUT: «ain't gonna happen␤» 
    

    Except that if they were really keywords, the precompiler would barf up jolly error messages when it encountered the definition of method WHAT. As it is, they are in a sort of half-and-half neither-one-thing-nor-another Limbo state.

    That's actually pretty normal. Many languages use "contextual keywords" that are valid identifiers, but have special meaning in some places.

    Which fits perfectly with my suggested concept of half-and-half neither-one-thing-nor-another Limbo state. I think so, anyway.

    Thinking about this some more, I realised that I was mixing up the concepts of "keywords" and "reserved words". int in C or C++ is a reserved word, while in (later versions of) C++, final is merely a (contextual) keyword. But it's still that sort of Limbo state.


  • Discourse touched me in a no-no place

    @Steve_The_Cynic said in The Raku Programming Language:

    Thinking about this some more, I realised that I was mixing up the concepts of "keywords" and "reserved words".

    It's pretty nasty that they have these as separate concepts.



  • @dkf said in The Raku Programming Language:

    @Steve_The_Cynic said in The Raku Programming Language:

    Thinking about this some more, I realised that I was mixing up the concepts of "keywords" and "reserved words".

    It's pretty nasty that they have these as separate concepts.

    Well, every language starts with someone writing a simple parser and lexer with a few reserved keywords. Then reality happens, but you don't want to break backwards compatibility or have half of the dictionary as reserved keywords and so you end up with a mix. It's a very common pattern.



  • @dfdub said in The Raku Programming Language:

    @dkf said in The Raku Programming Language:

    @Steve_The_Cynic said in The Raku Programming Language:

    Thinking about this some more, I realised that I was mixing up the concepts of "keywords" and "reserved words".

    It's pretty nasty that they have these as separate concepts.

    Well, every language starts with someone writing a simple parser and lexer with a few reserved keywords.

    Tell that to Fortran and PL/1.

           if if.eq.eq.or.or.eq.not then
    

    The Fortran parser is able to get its head round this one, although if I saw this in a code review (a) it would give me flashbacks (and not happy ones), and (b) the programmer proposing such code would get a good solid reaming, and he'd wish I had used the GAU-8.

    For reference, in C or C++, and upcasing the variables:

      if ( IF == EQ || OR == NOT )
    

    Then reality happens, but you don't want to break backwards compatibility or have half of the dictionary as reserved keywords and so you end up with a mix. It's a very common pattern.

    Or, worse, you end up changing the meaning of a previously-useless reserved word, with potentially ... interesting consequences.

    void f()
    {
      auto i = 1.0;  // No, you shouldn't ever write this in *any* version of C++, for reasons that are about to become clear.
    }
    

    In C++98 this means

    void f()
    {
      int i = 1.0; // some compilers might complain about double=>int assignment
    }
    

    In C++11, however, it means

    void f()
    {
      double i = 1.0;
    }
    

    That will cause some ... amusement later, and depending on how you use i, it might compile in one but not the other, and in any event it can end up calling different functions.


  • Discourse touched me in a no-no place

    @dfdub said in The Raku Programming Language:

    every language starts with someone writing a simple parser and lexer with a few reserved keywords

    No, but do I appreciate why you might think that.



  • @dkf
    I have no idea what you're trying to tell me (other than that I was obviously oversimplifying), but this sounds like the underhanded English version of an insult. I feel weirdly offended.



  • @Steve_The_Cynic said in The Raku Programming Language:

    auto i = 1.0; // No, you shouldn't ever write this in any version of C++, for reasons that are about to become clear.

    I strongly disagree with your comment. This is idiomatic C++11.



  • @dfdub said in The Raku Programming Language:

    @Steve_The_Cynic said in The Raku Programming Language:

    auto i = 1.0; // No, you shouldn't ever write this in any version of C++, for reasons that are about to become clear.

    I strongly disagree with your comment. This is idiomatic C++11.

    I despise it outside a few cases. For iterators auto it = vec.begin();, OK, because they are well-known. Are the rules for numeric literals well-known and universally definitive? (No.) For auto i = 1.0;, it's sort of OK (double), and equally for auto i = 1; (or any other number not exceeding 32767, giving int). However auto i = 40000; will be unsigned on platforms with 16-bit int(1) and int. on platforms with longer int.

    EDIT: Re: STL collection iterators... same for variables that will be some flavour of std::function because the declaration syntax for that is beyond awful (if it can be done at all by explicit declaration), and it's fairly evident that auto f = [](){}; or auto g = std::bind(...); create function-like things.

    There's also the small point that in order to read the cited line of code and know exactly what it will do, you have to know which version of C++ it will be compiled with. For the iterator case, it's not a problem, since it's well known what comes out of vec.begin() (and more to the point, that it is not compatible with int), so you know that it cannot be a version before 11.

    There are also a few FOSS codebases that are compilable with 98 or with 11, and they must not contain constructs like auto i = 1.0; because of the version-specific semantics.

    (1) There are still a few of those lurking around, although I doubt any of them can be effectively programmed in C++11.


  • Discourse touched me in a no-no place

    @dfdub said in The Raku Programming Language:

    @dkf
    I have no idea what you're trying to tell me (other than that I was obviously oversimplifying), but this sounds like the underhanded English version of an insult. I feel weirdly offended.

    My point was simply that not all programming languages have keywords. Some are formally keywordless. There are other ways to do languages.



  • @Steve_The_Cynic said in The Raku Programming Language:

    There are also a few FOSS codebases that are compilable with 98 or with 11

    Those seem to be becoming increasingly rare, though. Even many parts of Boost now require C++11. So unless you're working in one of those special projects, you shouldn't even have to think about what happens if someone compiles with the wrong version. That's an obvious user error; they should have checked the docs.

    If you want to be a bit more explicit, you can also write:

    auto i = double{1.0};
    

    But I think that's a bit verbose. In any case, starting variable declarations with auto is the new best practice and makes C++ "feel" more like most modern programming languages.



  • @dfdub said in The Raku Programming Language:

    @Steve_The_Cynic said in The Raku Programming Language:

    There are also a few FOSS codebases that are compilable with 98 or with 11

    Those seem to be becoming increasingly rare, though. Even many parts of Boost now require C++11. So unless you're working in one of those special projects, you shouldn't even have to think about what happens if someone compiles with the wrong version. That's an obvious user error; they should have checked the docs.

    Sadly, I am working with one of those projects. Well, not me ever since that part of the product got transferred to a different team, poor sods. I would argue that the version mismatch thing is a project error rather than a user error. How is it even possible to compile that project with the wrong language(1)? Don't they provide correct build resources (makefiles, automake doodads, CMakeLists.txt, etc.)? (Perhaps it's a good thing Blakey isn't around...)

    (1) There are enough differences between C++98 and C++11+ (especially C++17+) that they are starting to become like different languages.

    If you want to be a bit more explicit, you can also write:

    auto i = double{1.0};
    

    But I think that's a bit verbose. In any case, starting variable declarations with auto is the new best practice and makes C++ "feel" more like most modern programming languages.

    I like explicit. It's not really about making sure the compiler does the right thing, though. That's important, but communicating my intent to other programmers (including me six months from now) is more important. The more explicit I am (subject to some limits like the puts-the-ugh-in-ugly syntax of function objects and iterators), the less brain-work those other programmers have to expend just to know basic stuff about my code, like what type this variable is. It's especially important on application-specific types. Quick, what type is p in this line?

      auto p = doforit->nebble().fleeb->oojick();
    

    To be sure, a good IDE will explore the chain of calls and find it for me, but I can't just look at the code and see the type.


  • Discourse touched me in a no-no place

    @Steve_The_Cynic said in The Raku Programming Language:

    Quick, what type is p in this line?

    auto p = doforit->nebble().fleeb->oojick();
    

    To be sure, a good IDE will explore the chain of calls and find it for me, but I can't just look at the code and see the type.

    You can't even be sure what the -> and . operators are doing.



  • @dfdub In our code we strictly adhere to using {} instead of = for initialisations, exactly for reasons like this.

    void f()
    {
      int i{1.0}; // Compiler warning
      auto i{1.0}; // i is now of type std::initializer_list and can't be implicitly converted to a number
    }
    

    Oh, this is not really on-topic anymore eh



  • @dkf said in The Raku Programming Language:

    @Steve_The_Cynic said in The Raku Programming Language:

    Quick, what type is p in this line?

    auto p = doforit->nebble().fleeb->oojick();
    

    To be sure, a good IDE will explore the chain of calls and find it for me, but I can't just look at the code and see the type.

    You can't even be sure what the -> and . operators are doing.

    You're not wrong, but that's nothing specific to any version of C++.



  • @Grunnen The C++17 way is to always use both = and {}. If you compile as C++17 (which, unfortunately, we don't), you can always write auto var = type{initializer}; and it'll magically do the right thing.

    @Steve_The_Cynic said in The Raku Programming Language:

    I like explicit.

    And there's nothing wrong with that! However, repeating types over and over again, as in classic C++ and Java, is actually an antipattern. There's a reason comparable modern languages, like Rust and Kotlin, don't require this.

    It isn't just about verbosity. [0] There are more reasons not to name the type explicitly whenever you declare a variable:

    • Consistency. Just look at the average line of code and every implicit temporary that will be created in that line. Do you write explicit types anywhere? No? Then why are variable declarations (inside a function) so special? Now consider template code - no types in sight anywhere and we still manage to read and write it perfectly fine. (When we don't, it's not the lack of type information that's the problem.)
    • Readability. With type prefixes, it's incredibly hard to scan a function for variable declarations. You have to read half of the line to even get to the name of the variable! Why is the least important information (the type) the most prominent one? Why are the more important bits of information (name, initialization) given less visual priority? It simply doesn't make any sense.
    • Correctness. Type information being unnecessarily duplicated is not the only problem with not using auto, but actually the best case. In the worst case, copying the types everywhere introduces a source for bugs when your code changes. Remember, this is C++ - introduce a new operator definition or fancy pointer type somewhere and what you wanted to be a simple copy is suddenly a potentially costly and incorrect conversion. That's not what you want.

    But don't just listen to me. Look up what C++ gurus like Herb Sutter recommend [1] - they're all saying the exact same thing: Just use auto and type deduction.

    [0] Even though I think that's already a great argument - IDEs are a thing and even if you don't use one: If you cannot keep track of variable types inside a function, your code is too complex.
    [1] Example: https://github.com/CppCon/CppCon2014/blob/master/Presentations/Back to the Basics! Essentials of Modern C%2B%2B Style/Back to the Basics! Essentials of Modern C%2B%2B Style - Herb Sutter - CppCon 2014.pdf (page 13ff)



  • @dkf said in The Raku Programming Language:

    You can't even be sure what the -> and . operators are doing.

    . can't be overloaded.

    But, alas, you also don't know what nebble() or oojick() is doing, so I don't see why you'd get hung up on only operator->(). At least it has a name that means something, unlike the two methods with nonsensical names.


  • BINNED

    @dfdub said in The Raku Programming Language:

    Just use auto and type deduction.

    While I do that a lot now, it can be hazardous if the deduction is to some expression type (see expression templates) that you’d actually want converted at the end of the declaration.



  • @Steve_The_Cynic said in The Raku Programming Language:

    However auto i = 40000; will be unsigned on platforms with 16-bit int(1) and int. on platforms with longer int.

    Not in my tests, using the Arduino Uno compiler (which is not a 32 bit platform, see values for INT_MAX and UINT_MAX).

    Here, auto i = 40000; turns i into a long int and not an unsigned. Personally, I would have hoped for a warning/error, that tells me to use 40000l instead. auto i = int{40000} as suggested further downthread does that.

    @Steve_The_Cynic said in The Raku Programming Language:

    (1) There are still a few of those lurking around, although I doubt any of them can be effectively programmed in C++11.

    As demonstrated above, Arduino uses a C++17 compatible compiler, and I've been using C++11 and later for those chips quite happily. The standard library for C++ is a bit MIA, but that holds true for other versions as well.

    There's also this video by Jason turner, where he writes a small game (pong) in C++17 for a Commodore 64.



  • @cvi said in The Raku Programming Language:

    . can't be overloaded.

    There was a discussion on whether it should be allowed, though. Would have been cool for reference_wrapper, but probably have made a lot of other stuff unnecessarily complicated. Having to think about overloaded unary & operators is bad enough.



  • @cvi said in The Raku Programming Language:

    @Steve_The_Cynic said in The Raku Programming Language:

    However auto i = 40000; will be unsigned on platforms with 16-bit int(1) and int. on platforms with longer int.

    Not in my tests, using the Arduino Uno compiler (which is not a 32 bit platform, see values for INT_MAX and UINT_MAX).

    Any implementation of C++ that would deduce the type of an integer literal without a u suffix to be unsigned would be both non-conforming and brain-dead and should be nuked from orbit.


  • Discourse touched me in a no-no place

    @dfdub said in The Raku Programming Language:

    made a lot of other stuff unnecessarily complicated

    When has that ever been a hindrance for the C++ standards committee?



  • @cvi said in The Raku Programming Language:

    @Steve_The_Cynic said in The Raku Programming Language:

    However auto i = 40000; will be unsigned on platforms with 16-bit int(1) and int. on platforms with longer int.

    Not in my tests, using the Arduino Uno compiler (which is not a 32 bit platform, see values for INT_MAX and UINT_MAX).

    Here, auto i = 40000; turns i into a long int and not an unsigned. Personally, I would have hoped for a warning/error, that tells me to use 40000l instead. auto i = int{40000} as suggested further downthread does that.

    Ugh. It's been too long since I needed to know this stuff. Either way, though, it isn't int.

    @Steve_The_Cynic said in The Raku Programming Language:

    (1) There are still a few of those lurking around, although I doubt any of them can be effectively programmed in C++11.

    As demonstrated above, Arduino uses a C++17 compatible compiler, and I've been using C++11 and later for those chips quite happily. The standard library for C++ is a bit MIA, but that holds true for other versions as well.

    There's also this video by Jason turner, where he writes a small game (pong) in C++17 for a Commodore 64.

    I sit corrected.



  • @Steve_The_Cynic said in The Raku Programming Language:

    Either way, though, it isn't int.

    Yeah, true.

    To be fair, while testing int i = 40000; was silently accepted (no warning/error with -Wall -Wextra), which also seems dangerous. OTOH auto i = int{40000}; produced an error, which to me seems the most safe option.



  • @dfdub said in The Raku Programming Language:

    There was a discussion on whether it should be allowed, though. Would have been cool for reference_wrapper, but probably have made a lot of other stuff unnecessarily complicated. Having to think about overloaded unary & operators is bad enough.

    Yeah. I think I've seen it discussion a few times. There are a couple of ideas that relate to it that seem to be a bit at odds (e.g. universal calling syntax, and a recent paper that proposed a new syntax/operator even, but that had some other neat ideas). As you say, there would be a few good applications, but it also opens up for a pile of new pitfalls.



  • @cvi said in The Raku Programming Language:

    @Steve_The_Cynic said in The Raku Programming Language:

    Either way, though, it isn't int.

    Yeah, true.

    To be fair, while testing int i = 40000; was silently accepted (no warning/error with -Wall -Wextra), which also seems dangerous.

    Indeed. That's actually nasty, because I suspect that relying on the value of i afterwards is UB (on a 16-bit int system), and in any event a hazard because a 1's complement system and a 2's complement system will give different values for casting 40000 to int.

    OTOH auto i = int{40000}; produced an error, which to me seems the most safe option.

    Does that force i to be really an int rather than a std::initializer_list?



  • @dfdub said in The Raku Programming Language:

    @cvi said in The Raku Programming Language:

    @Steve_The_Cynic said in The Raku Programming Language:

    However auto i = 40000; will be unsigned on platforms with 16-bit int(1) and int. on platforms with longer int.

    Not in my tests, using the Arduino Uno compiler (which is not a 32 bit platform, see values for INT_MAX and UINT_MAX).

    Any implementation of C++ that would deduce the type of an integer literal without a u suffix to be unsigned would be both non-conforming and brain-dead and should be nuked from orbit.

    Even for an ILP32 system? (int, long and pointers all 32-bit)

    auto i = 3000000000; // nine zeros, three short-scale billion, greater than 2**31-1, therefore not representable in int32_t.
    

    Here, a signed version of i cannot represent the value given. What should the compiler do in such a case? Make i have type long long (or other 64-bit integer type)? And yes, I know, the answer is, "well, don't do that..."



  • @Steve_The_Cynic said in The Raku Programming Language:

    and in any event a hazard because a 1's complement system and a 2's complement system will give different values for casting 40000 to int.

    Good news: The C++20 standard now guarantees two's complement for signed integers.

    @Steve_The_Cynic said in The Raku Programming Language:

    Does that force i to be really an int rather than a std::initializer_list?

    Yes. The only way to get a variable of type std::initializer_list is to write auto my_var = {<list of values of same type>};. Which in 99% of all cases would be an easily detectable accident. I've never seen anybody do that on purpose.

    Brace-initialization has almost nothing to do with std::initializer_list, that's just a remotely related feature for initializing containers that happens to share the same syntax.

    Here, a signed version of i cannot represent the value given. What should the compiler do in such a case?

    It chooses the largest signed integer type that can represent the value. If such a type doesn't exist, you should get a compile-time error.



  • @Steve_The_Cynic said in The Raku Programming Language:

    What should the compiler do in such a case?

    int64 x = 3G

    But this kind of situation is why I'm really not a fan of auto-typing for numbers. It is actually important what numeric type you're using.

    Using autotyping for (apologies for incorrectness in the following as I'm not a C++ guy)

    std::iterator<std::list<MyClass<int>>> x = listOfThings->iterator();
    

    ... well, that makes sense, because

    auto x = listOfThings->iterator();

    ... is easier to read and little information is lost. But there is a big difference between "int x = 100", "long x = 100" and "double x = 100", and the type word is short, so you are not really helping readability by replacing the type word and the information you lose is important.

    The same applies with 'var' in C#. 'var x = new Dictionary<String, MyClass<int>>' is a QOL improvement. 'var x = 10' is confusing.

    Of course JS gets away with it because there, when you do 'let x = 10', nobody really knows or cares what numeric type that '10' is.



  • @Steve_The_Cynic said in The Raku Programming Language:

    There are also a few FOSS codebases that are compilable with 98 or with 11

    I made mine error out unless you're on at least 14.



  • @dcon said in The Raku Programming Language:

    @Steve_The_Cynic said in The Raku Programming Language:

    There are also a few FOSS codebases that are compilable with 98 or with 11

    I made mine error out unless you're on at least 14.

    The ones I'm thinking of are notionally C++98 but constructed so that they function correctly(1) if compiled with 11 or 17. (Or probably others as well.)

    (1) Well, that's a questionable statement, because in one case, I'm not convinced it functions correctly no matter how it's compiled. Sadly, by the time I got anywhere near it (a) the guys who included it in our codebase had already left the company, and (b) it was baked hard into the component they had been building. There's still an open ticket in our bug system to investigate removing it.



  • @dfdub said in The Raku Programming Language:

    @Steve_The_Cynic said in The Raku Programming Language:

    and in any event a hazard because a 1's complement system and a 2's complement system will give different values for casting 40000 to int.

    Good news: The C++20 standard now guarantees two's complement for signed integers.

    Finally! Took them bloody long enough. Has someone finally taken all the CDC Cybers round the back of the building and put a bullet through them?

    @Steve_The_Cynic said in The Raku Programming Language:

    Does that force i to be really an int rather than a std::initializer_list?

    Yes. The only way to get a variable of type std::initializer_list is to write auto my_var = {<list of values of same type>};. Which in 99% of all cases would be an easily detectable accident. I've never seen anybody do that on purpose.

    I asked precisely because that syntax had been mentioned earlier.

    Brace-initialization has almost nothing to do with std::initializer_list, that's just a remotely related feature for initializing containers that happens to share the same syntax.

    Don't get me started on brace-initialization. For actual initialization of named classish objects, it seems mostly just noise, but there are certain cases where :wtf:a blindly religious "all object construction shall be by brace initialization because some weird corner case goes wrong if you don't":wtf:(1) creates really weird syntax.

    Here's a distinctly niche, but real example from my company's own codebase:

       boost::string_view strview = (stuff that doesn't matter, probably a function parameter);
    
       // later, some use of strview that *implicitly* autoconstructs a temporary std::string
    

    On removing boost::string_view in favour of std::string_view which doesn't have any such implicit (or explicit) construction, and some code that must call a method of the std::string:

       std::string_view strview = (stuff that doesn't matter, probably a function parameter);
    
       printfy_function("format string with %s", std::string{ strview }.c_str());
    

    Brace-dot ?????????????????????????

    Here, a signed version of i cannot represent the value given. What should the compiler do in such a case?

    It chooses the largest signed integer type that can represent the value. If such a type doesn't exist, you should get a compile-time error.

    I don't like that "should" in there. It does error out, or it does not error out. Or, to misquote a small green pointy-eared ungreat unwarrior, "do, or do not, there is no should".

    (1) I hope this reveals where I sit on that particular debate. (There's a separate debate about the wisdom of string_view in the first place, but I won't get into that here.)

    EDIT: My observation is that it's a type coercion that achieves that result by constructing a temporary object, rather than really wanting a temporary object.



  • @Steve_The_Cynic said in The Raku Programming Language:

    @dfdub said in The Raku Programming Language:

    @Steve_The_Cynic said in The Raku Programming Language:

    Does that force i to be really an int rather than a std::initializer_list?

    Yes. The only way to get a variable of type std::initializer_list is to write auto my_var = {<list of values of same type>};. Which in 99% of all cases would be an easily detectable accident. I've never seen anybody do that on purpose.

    I asked precisely because that syntax had been mentioned earlier.

    There's a huge difference between the following two lines:

    auto i = my_type{1, 2}; // equivalent to my_type i{1, 2}; in C++17
    auto j = {1, 2}; // std::initializer_list<int>, a.k.a. likely a bug
    

    I was arguing for the former.

    On removing boost::string_view in favour of std::string_view which doesn't have any such implicit (or explicit) construction, and some code that must call a method of the std::string:

       std::string_view strview = (stuff that doesn't matter, probably a function parameter);
    
       printfy_function("format string with %s", std::string{ strview }.c_str());
    

    Brace-dot ?????????????????????????

    :trwtf: is that someone doesn't know that std::string_view has a data() function. That whole construct is completely unnecessary in the first place unless you want to waste CPU cycles. Even if the underlying string is not null-terminated, you don't construct a temporary std::string just to get a null byte at the end of your copy of the string.

    Here, a signed version of i cannot represent the value given. What should the compiler do in such a case?

    It chooses the largest signed integer type that can represent the value. If such a type doesn't exist, you should get a compile-time error.

    I don't like that "should" in there. It does error out, or it does not error out. Or, to misquote a small green pointy-eared ungreat unwarrior, "do, or do not, there is no should".

    "Should" means "@dfdub is too lazy to look it up in the standard" in this context, not that the standard leaves the behavior unspecified.

    EDIT: My observation is that it's a type coercion that achieves that result by constructing a temporary object, rather than really wanting a temporary object.

    Nope, the integer literal already has the target type. No temporaries are constructed here (from C++17 at least, before there might be an extra copy if the compiler is dumb) and no type conversion happens.



  • @dfdub said in The Raku Programming Language:

    @Steve_The_Cynic said in The Raku Programming Language:

    On removing boost::string_view in favour of std::string_view which doesn't have any such implicit (or explicit) construction, and some code that must call a method of the std::string:

       std::string_view strview = (stuff that doesn't matter, probably a function parameter);
    
       printfy_function("format string with %s", std::string{ strview }.c_str());
    

    Brace-dot ?????????????????????????

    :trwtf: is that someone doesn't know that std::string_view has a data() function. That whole construct is completely unnecessary in the first place unless you want to waste CPU cycles. Even if the underlying string is not null-terminated, you don't construct a temporary std::string just to get a null byte at the end of your copy of the string.

    It starts to look messy to get the right behaviour because the string_view: might be a view of the middle of a NUL-terminated string, with some characters after what the view is viewing.

       std::string_view strview = (stuff that doesn't matter, probably a function parameter);
    
       printfy_function("format string with %.*s", static_cast<int>(strview.length()), strview.data());
    

    The cast to int is required because the "*" field-width specifier requires it.

    EDIT: My observation is that it's a type coercion that achieves that result by constructing a temporary object, rather than really wanting a temporary object.

    Nope, the integer literal already has the target type. No temporaries are constructed here (from C++17 at least, before there might be an extra copy if the compiler is dumb) and no type conversion happens.

    My EDIT was about the string_view part of the discussion. To be sure, it constructs a temporary object, but the reason for doing so (as seen by the programmer) is "to coerce the type" rather than "to construct a temporary". In boost::string_view, there's a member function that returns the constructed string (and therefore the question doesn't arise(1)), but std::string_view doesn't have that.

    (1) The question about whether converting to std::string is the right thing to do still arises.


  • Considered Harmful

    @Steve_The_Cynic said in The Raku Programming Language:

    To be sure, a good IDE will explore the chain of calls and find it for me, but I can't just look at the code and see the type.

    A good IDE will show the type right next to the variable declaration, so you can just look at the code and see the type.


  • Considered Harmful

    @dfdub said in The Raku Programming Language:

    Just look at the average line of code and every implicit temporary that will be created in that line. Do you write explicit types anywhere? No?

    Rust is actually going to get generalized ascription soon. This will let you suffix any expression with : <type goes here>, like x.to_string(): String, while still being able to dot-chain after it or encase it in parentheses, for the exact purpose you just mentioned.


  • BINNED

    @Steve_The_Cynic said in The Raku Programming Language:

    Brace-initialization has almost nothing to do with std::initializer_list, that's just a remotely related feature for initializing containers that happens to share the same syntax.

    Don't get me started on brace-initialization. For actual initialization of named classish objects, it seems mostly just noise, but there are certain cases where :wtf:a blindly religious "all object construction shall be by brace initialization because some weird corner case goes wrong if you don't":wtf:(1) creates really weird syntax.

    Back 9 years ago "uniform initialization" was touted to resolve all kinds of problems, but in my subjective opinion it was more of an xkcd://standards that did more to proliferate the Bubba-Gump-Shrimps initialization than it did to remove it.
    Worst part is when parenthesis-init and brace-init actually behave differently and you need to dig to figure out the difference. I think (but don't remember exactly) there's even some cases where A()does value-initialization and A{} does aggregate-initialization, leaving some members uninitialized.
    Or, in short: "uniform initialization" isn't.

    Here's a distinctly niche, but real example from my company's own codebase:

    std::string{ strview }.c_str());
    

    Brace-dot ?????????????????????????

    🤢


  • :belt_onion:

    @dfdub said in The Raku Programming Language:

    Just like Microsoft word automatically replaces three dots with an ellipsis.

    Or "quotes with ""smart quotes """

    Because that never got on anyone's nerves...


  • :belt_onion:

    Also @pie_flavor thanks for this thread. This is your prize for introducing this into my life:

    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHHHHH



  • @sloosecannon said in The Raku Programming Language:

    "smart quotes"

    FTFY


  • :belt_onion:

    @caffiend said in The Raku Programming Language:

    even Python has this problem when not everyone on the team uses the same tabstop settings

    No no no, see.
    The problem with that is that Python took something that was traditionally completely irrelevant, and common practice in every other language to mangle/standardize/reduce to save space/do whatever to, because it didn't matter, and made it central to the language.


  • :belt_onion:

    @Zerosquare said in The Raku Programming Language:

    @sloosecannon said in The Raku Programming Language:

    "smart quotes"

    FTFY

    Thank you. """""Smart quotes"""""" are difficult to insert on mobile


  • :belt_onion:

    @pie_flavor said in The Raku Programming Language:

    @Steve_The_Cynic said in The Raku Programming Language:

    To be sure, a good IDE will explore the chain of calls and find it for me, but I can't just look at the code and see the type.

    A good IDE will show the type right next to the variable declaration, so you can just look at the code and see the type.

    Ah, another jetbrains user I see...


  • Discourse touched me in a no-no place

    @sloosecannon said in The Raku Programming Language:

    @caffiend said in The Raku Programming Language:

    even Python has this problem when not everyone on the team uses the same tabstop settings

    No no no, see.
    The problem with that is that Python took something that was traditionally completely irrelevant, and common practice in every other language to mangle/standardize/reduce to save space/do whatever to, because it didn't matter, and made it central to the language.

    Python has many problems. The significant whitespace is rarely one of them (except when cut-n-pasting code).


  • :belt_onion:

    @dkf said in The Raku Programming Language:

    @sloosecannon said in The Raku Programming Language:

    @caffiend said in The Raku Programming Language:

    even Python has this problem when not everyone on the team uses the same tabstop settings

    No no no, see.
    The problem with that is that Python took something that was traditionally completely irrelevant, and common practice in every other language to mangle/standardize/reduce to save space/do whatever to, because it didn't matter, and made it central to the language.

    Python has many problems. The significant whitespace is rarely one of them (except when cut-n-pasting code).

    I know, but it makes my brain explode so I make fun of it every chance I get


Log in to reply