[HN Gopher] Microfeatures I'd like to see in more languages
       ___________________________________________________________________
        
       Microfeatures I'd like to see in more languages
        
       Author : Tomte
       Score  : 119 points
       Date   : 2023-01-05 20:29 UTC (2 hours ago)
        
 (HTM) web link (buttondown.email)
 (TXT) w3m dump (buttondown.email)
        
       | tobyhinloopen wrote:
       | Elixir's testing library uses meta programming to show the code
       | that fails and what the values were at both sides of a
       | comparison.
       | 
       | IE                   a = 1; b = 2         assert a == b
       | 
       | Will fail with error like:                   Assertion failed,
       | a == b         Left is 1         Right is 2
       | 
       | So you don't have a bunch of assert-functions; you just assert
       | anything and it will spit out a decent error.
        
         | elcritch wrote:
         | Yes! That feature is great. I got used to it in Elixir and Nim
         | provides it as well. It's one of those little things that makes
         | programming nicer.
        
         | masklinn wrote:
         | With how old pytest is, I assume that's where they got it from.
         | Does it perform recursive value printing, or bespoke
         | comparisons?
         | 
         | e.g. in pytest it won't just print out the values of "a" and
         | "b", it will recursively document intermediate values until
         | it's reached the toplevel expression:                   assert
         | f() == g()         assert 42 == 43           where 42 =
         | <function TestFailing.test_simple.<locals>.f at
         | 0xdeadbeef0002>()           and   43 = <function
         | TestFailing.test_simple.<locals>.g at 0xdeadbeef0003>()
         | 
         | and it's possible to customise the report so you can report as
         | a diff:                   assert "foo 1 bar" == "foo 2 bar"
         | - foo 2 bar           ?     ^           + foo 1 bar           ?
         | ^
        
       | 4ad wrote:
       | > Instead of writing 10000500, you can write 10_000_500, or
       | 1_00_00_500 if you're Indian.
       | 
       | I hate this so much. It means I can't grep for a constant.
        
         | overengineer wrote:
         | 1_?0_?00_?0_?500 yes it sucks
        
           | elygre wrote:
           | That's not enough -- the underscores can appear anywhere, so
           | you need to cater for 500000_0 and 5_0_0_0_0 etc. basically
           | an optional underscore between each digit.
        
             | amalgamated_inc wrote:
             | just grep for `[0-9_]+`
        
               | masklinn wrote:
               | I would assume they're looking for a specific constant
               | value.
        
         | turtledragonfly wrote:
         | Well you can, you just need a more complicated regex. Same for
         | '1e6' and such.
        
         | Smaug123 wrote:
         | Out of interest, how often do you do this, and what are the
         | semantics of the numbers you're grepping for? I literally can't
         | remember a time I've ever tried to grep for a number.
        
           | duped wrote:
           | I've done it for error codes but that was awfully cursed
        
             | jbverschoor wrote:
             | But you need to search for both hex and dec codes for that
        
         | maxbond wrote:
         | If you have constants repeated throughout the codebase, you can
         | pull them into a constants file, and then you can go to that
         | file, navigate to the definition of interest, and use your IDE
         | of choice to find usages.
         | 
         | You'll also be able to give these constants semantically
         | significant names, and comment next to them providing
         | derivations or citations. And of course, if it's a mistaken or
         | outdated value, you can change it one place and apply it
         | everywhere.
         | 
         | Consider that, if you were debugging a problem with this
         | constant, and the problem was caused by someone having made a
         | typo in one of it's usages (eg having typed 1000500 instead of
         | 10000500, a mistake that's more difficult to make of you have
         | better ways to format numbers [did you have to look back and
         | forth to find the mistake? I did]) - your regex would fail to
         | find it, even if there were no ambiguity about the format it
         | was written in.
        
           | ludston wrote:
           | Depends on the size of your codebase and how many people work
           | on it. Once you have 20 years of code written full time by
           | 200 developers in your monolith, finding the constants file
           | out of 400 different subsystem's constant files for a given
           | subsystem that you've never seen before can become a
           | legitimate and challenging pain.
        
             | maxbond wrote:
             | Totally true. I don't know if IDEs commonly have a feature
             | like, "find this value, regardless of how it's expressed"
             | (even better yet, fuzzily, to catch typos), but I think
             | that's the proper general solution. It's a good idea to
             | consider a constants file earlyish, while everything still
             | fits in your head.
             | 
             | I'd say in the case of such a sprawling system, make a
             | constants module, and it can have different files for
             | different topics. But keep them all together. Code style is
             | an engineering tool you can use to prevent problems.
             | 
             | But I do understand this is cold comfort for those working
             | on systems where the decision around this were made 15
             | years ago, and there's no possibility of refactoring the
             | constants. That's quite annoying.
        
               | CuriouslyC wrote:
               | Then you have a problem with having to update both the
               | constants module and the service that depends on it to
               | change the service, and you need to handle packaging and
               | distributing the module. Maybe if you have a monorepo
               | where those issues are moot...
        
               | maxbond wrote:
               | I didn't mean to suggest the constants module was a
               | separate, reusable module, but a component of the same
               | piece of software. By "module" here I meant "directory
               | which can contain multiple importable files," so you
               | could namespace your constants (constants/rfc_abcd.xyz,
               | constants/customer_limits.xyz, etc).
               | 
               | I'd rather copy-paste any reused constants to different
               | projects to avoid coupling, unless there was some kind of
               | compelling domain/project specific reason.
        
               | ludston wrote:
               | When your code-base reaches a certain size, you cease
               | using the IDE to find code and instead start using
               | specialized tools, such as Lucene indexes so that
               | grepping through code takes seconds rather than minutes.
               | One of the bads of this is that using regex over an index
               | is O(n) in comparison to the O(log(n)) of a normal index
               | lookup.
        
               | maxbond wrote:
               | Perhaps this special tooling can normalize numbers and
               | other source code ambiguity.
        
             | CuriouslyC wrote:
             | This so hard. A constant that replaces a magic number like
             | 5 that is ungreppable, or a constant for a precise number
             | like the physical constants - sure. A constant that
             | replaces a number like 404 or 500 that you want to be able
             | to easily grep across heterogenous code bases for? Pass.
        
               | maxbond wrote:
               | Is that 500 the HTTP error or 500 the adhoc limit we put
               | on the number of user uploads or a 500, representing half
               | a kilobyte?
        
               | CuriouslyC wrote:
               | I'll take the momentary ambiguity in some cases (usually
               | quickly resolved by line context and file name) over
               | having to manually hunt down 5 different projects'
               | inconsistently named constants to do 5 different greps
               | any day of the week.
        
           | progval wrote:
           | I'm in the same boat as GP. Typically, I grep such things
           | when I am not familiar with the codebase, so I can't change
           | where constants are defined, and I do not know where to find
           | such a file.
        
             | maxbond wrote:
             | For what it's worth when I dive into a new codebase, the
             | first thing I do is try to guess what files exist and then
             | find them and get a feel for structure. Constants are high
             | in the list.
             | 
             | But there are many ways to skin a cat.
        
           | 4ad wrote:
           | You haven't understood the problem at all. No wonder, few
           | people do any sort of bare metal programming. It's not about
           | defining constants, or even writing code at all, _it 's about
           | figuring out what this arbitrary piece of code is doing based
           | on hardware datasheets_. You search for constants defined in
           | the datasheet in the piece of code you are analyzing...
        
         | dan-robertson wrote:
         | The funny way I've seen this go wrong in practice is a typo
         | like:                 // we don't want more than 100m because
         | ...       quota = 1000_000_000
         | 
         | Where people assume the underscores are in the expected place.
        
         | AnimalMuppet wrote:
         | You already can't, because someone can write the constant in
         | hex, or even (shudder) octal.
        
           | adrianmonk wrote:
           | Or like this:                  const int
           | WAIT_TIME_MICROSECONDS = 42 * 1000 * 1000; // 42 seconds
        
       | perlgeek wrote:
       | Some from Raku (formerly Perl 6) that I really like:
       | 
       | * sub MAIN:
       | 
       | sub MAIN(Int $x, :$verbose) { }
       | 
       | generates a command line parser that expects an Integer plus an
       | optional named switch --verbose
       | 
       | * It has named params (as seen above), and there are
       | abbreviations: instead of thing => $thing you can write :$thing
       | to avoid duplicating the name (:thing also exists, though it
       | create a pair "thing" => True, so ruby lovers need to be careful
       | :D )
       | 
       | * junctions for quick conditionals/validation: 0 <= all($x, $y,
       | $z) <= 2 * pi
       | 
       | * this is a probably debatable, but: if you use a * as a term, it
       | will create a lambda for you, so *+2 is similar to sub ($x) { $x
       | + 2 }
        
       | amalgamated_inc wrote:
       | Elixir's sigils are amazing. There are date sigils that allow you
       | to do what the OP does: ~N[2023-01-01 12:00:00]
       | 
       | But you can also define your own sigils to create new "custom
       | syntax" for almost any struct. Kind of a special case of reader
       | macros, I guess. Very convenient.
        
         | mncharity wrote:
         | Elixir sigils[1][2].
         | 
         | Eg: ~w(foo bar bat) is a word list. `~ letter bracketed-text
         | lettersasmodifiers` desugars as sigil_<letter>(text,modifiers).
         | Similar to foo_str() of Julia[3], but for one-letter-names and
         | more brackets. But not the unicode brackets of Raku.
         | 
         | [1] https://elixir-lang.org/getting-started/sigils.html [2]
         | https://hexdocs.pm/elixir/main/syntax-reference.html#sigils [3]
         | https://docs.julialang.org/en/v1/manual/metaprogramming/#met...
        
         | cschmatzler wrote:
         | The most recent addition to the digit family being Phoenix's
         | new ~p"/healht", which is a HTTP route string that
         | automatically verifies whether the route exists, and returns
         | compile-time warnings when you link to a path that didn'
         | doesn't. It's fantastic, and really surprising it took this
         | long to be added to any web framework.
        
       | nicoburns wrote:
       | One of the best truly micro features I've seen recently (can't
       | remember which langauge unfortunately - it wasn't a mainstream
       | one), is general binary literal syntax of the form:
       | 0x[de ad be ef 00]
       | 
       | So much nicer than the usual condensed format. And I think it'd
       | be valid syntax in any language that allows binary integer
       | literal.
        
         | andix wrote:
         | A lot of languages allow underscores in numeric literals.
         | Something like 10_000. You can put them anywhere and they get
         | ignored. I don't know if they also allow it for hex numbers.
        
           | masklinn wrote:
           | > I don't know if they also allow it for hex numbers.
           | 
           | They do e.g. Python                   >>> 0x_ab_cd_01_23
           | 2882339107
           | 
           | or                   >>> 0b_0010_0100         36
        
             | rwmj wrote:
             | Even C these days, although they chose ' instead of _ :-(
        
           | [deleted]
        
         | jbverschoor wrote:
         | ohhh that's cool.. in Ruby you can do 0x_dead_beef_00 or any
         | other combo
        
         | dan-robertson wrote:
         | Is that giving you a number or a bytestring?
        
         | mncharity wrote:
         | I like that Elixir bitstrings[1] don't have to be bytes.
         | <<2:3>> is three bits 010.
         | 
         | [1] https://elixir-lang.org/getting-started/binaries-strings-
         | and...
        
       | Doctor_Fegg wrote:
       | Negative array subscripts. So a[-1] means the last element of an
       | array, a[-2] means the second last, and so on.
        
         | tester756 wrote:
         | if it works on literals only, so a[x] doesnt work if x is
         | negative, then ok.
         | 
         | otherwise seems like an errors that are hard to spot.
        
         | _a_a_a_ wrote:
         | As a choice then perhaps but as a default and unalterable
         | behaviour it can be a bloody timewaster when negative
         | subscripts are a runtime error in your work. I've hit that in
         | python and didn't enjoy it.
        
         | scubbo wrote:
         | I was really surprised when I learned my second language (after
         | Python) to realize that this wasn't standard!
        
         | unnouinceput wrote:
         | That's for languages that can't define arrays with custom
         | start/stop indexes. But those that have custom indexes they can
         | very easily expand/implement as helper class (for example
         | array.indexFromLast(1) which means array[Length(array)]. This
         | way you can have best of both worlds.
        
           | masklinn wrote:
           | Surely if your language has custom indexes / ranges
           | `Length(array)` is completely broken and the language
           | provides something like "Index`Last" you can hook on?
           | 
           | Because an array with indexes [3, 7) has length 4, but 4 is
           | not the index of the last element.
        
             | Jtsummers wrote:
             | * * *
        
             | unnouinceput wrote:
             | Yup, correct. What I meant above with array[Length(array)]
             | is for the languages that don't have it. Let me be more
             | clear.
             | 
             | C/C++ doesn't have custom array indexes and as such
             | <array[std::size(array) - 1]> is returning the last element
             | of said array.
             | 
             | Delphi has custom array indexes and as such, taking your
             | example with defining an array in the form <example_array :
             | array[3..7] of integer>, I would not get the last element
             | in case of <example_array[Length(example_array) - 1]. In
             | this case I would have 2 options. Option 1 would be to use
             | <High> function as in <example_array[High(example_array)]>
             | to access example_array[7] element. Delphi also has <Low>
             | function so you can iterate through a custom defined array
             | by using <for> keyword with the help of them. Option 2
             | would be to actually build my own helper (this is the most
             | wanted case when you're dealing with multi-dimensional
             | arrays that also have custom indexes) and I would have
             | something like <example_array.FromLastIndex(0)> to access
             | example_array[7] element.
             | 
             | Hope this cleared the confusion.
        
             | kqr wrote:
             | Yes, then I would expect 'First and 'Last with the obvious
             | meaning, and something like 'Range which returns an
             | iterator of all indices.
        
         | feoren wrote:
         | Any language that supports overriding the index operation
         | should support this. You should be able to do this in C# with a
         | struct with a backing array, for instance. If you're going to
         | do this, use the word "Circular" in it, and I would also insist
         | that if a has 4 elements, then a[0] == a[4] == a[8]. In other
         | words, you always just take the (positive) index modulo the
         | size of the area. Then a[-1] is the same as a[N-1] for an array
         | of size N. This could be useful in a lot of contexts, but
         | should be made explicit.
        
           | anamexis wrote:
           | Where would the circular indexing like that be useful?
        
             | AlotOfReading wrote:
             | It's frequently used in signal processing to the point
             | where it's considered one of the defining features of DSPs.
             | One common case is filtering over a fixed size buffer of
             | samples. If you have circular indexing, you can simply
             | overwrite the earliest sample and increment the base
             | reference to the next element.
             | 
             | I'm not sure I'd want it for every list, but there are
             | certain places it's nice.
             | 
             | [1] https://www.allaboutcircuits.com/technical-
             | articles/circular...
        
             | feoren wrote:
             | First of all, it handles the "get me the 2nd to last
             | element" case automatically, but in a way that doesn't feel
             | like a weird edge case: it's more "mathematically sound",
             | basically. I always want mathematical soundness if possible
             | because it leads to serendipity, the opposite of technical
             | debt. Where technical debt is "dammit, this is going to
             | take so much longer than it should!"; serendipity is "oh
             | wow I can implement this cool new feature just by combining
             | these other two things in a new way, in like 2 lines. This
             | is going to be way faster than I thought." Mathematical
             | soundness / purity leads to serendipity.
             | 
             | Directly, it supports caches very well. You just increment
             | the number of things you've ever cached and that's where
             | your next cached value goes; you don't care when it
             | overwrites an old value.
             | 
             | There are other cases where you just need some variant of a
             | thing, but you don't actually care that much about which
             | variant you get. You might want to vary your wording in
             | auto-generated text, for instance, by rotating synonyms. Or
             | rotating the tiles you use in a 2D game. In this case I'd
             | define an interface where you pass in a "seed" integer and
             | it gives you back some deterministic example; a circular
             | array is the simplest implementation of this interface (but
             | there are others).
             | 
             | You could also do simple load balancing by sending work to
             | Worker[workCount++]. While usually you want to track each
             | workers' existing workload (because the work takes
             | unpredictable time), this simple approach could be
             | sufficient if all your work completes in about the same
             | time.
             | 
             | If you're doing fancy math or science computing, you may be
             | working with finite groups or fields, whose elements you
             | could stick in an N-dimensional circular array (based on
             | the characteristics of the field).
        
           | _a_a_a_ wrote:
           | What 'mod' is for, innit.
        
           | philsnow wrote:
           | You'd have to care about the difference between indexing with
           | a literal and indexing with variables of different
           | types/widths, and how indexing with a variable interacts with
           | the size of the area.
           | 
           | For instance if you have an int array that contains the
           | numbers 1-250 and you index with a uint8 variable i,
           | for (uint8 i = 247; i++;) {         // print circ_arr[i]
           | }
           | 
           | for the values of i near the overflow points of the circular
           | array and of the uint8 it gets weird:                 i
           | circ_arr[i]       247  247       248  248       249  249
           | 250  250       251  1   # 251 % 250 = 0       252  2   # 252
           | % 250 = 1       253  3   # ...       254  4       255  5
           | 0    1   # i overflows to 0       1    2       ...
        
       | mr_mitm wrote:
       | I really liked the postfix and prefix notation in Mathematica.
       | These three all mean the same:
       | 
       | f[x]
       | 
       | f@x
       | 
       | x // f
       | 
       | It matches the flow of thought more naturally when hammering out
       | a couple of one-liners.
        
         | japanman425 wrote:
         | How is that third one readable at all? I'd assume it meant
         | integer division.
        
           | mr_mitm wrote:
           | That's just a matter of habit. The exact symbol isn't
           | important anyway.
        
           | Smaug123 wrote:
           | Mathematica uses a _lot_ of syntactic sugar. You will find
           | _all_ Mathematica code unreadable until you 've learned to
           | read it; `//` is no exception.
        
             | contravariant wrote:
             | I find Mathematica code unreadable full stop.
             | 
             | Its convenient when writing though.
        
               | mr_mitm wrote:
               | I see Mathematica as a shell for math. You can write long
               | programs or modules in it, sure, but very often you're
               | simply typing up a couple of lines to check some
               | computation or visualize an expression which you won't
               | even save, in which case readability is secondary.
        
           | carry_bit wrote:
           | It's usually used with the formatting functions, so you have
           | something like:
           | 
           | <Some big expression here> // Column
           | 
           | Useful where the function in question is an "afterthought".
        
           | rwmj wrote:
           | You're writing something and you decide you want to apply 'f'
           | to it, so you type '// f' (instead of backspacing like a
           | caveman). It's actually rather convenient.
        
           | [deleted]
        
       | orangepanda wrote:
       | Php supports kebab-case variables:                   ${"variable-
       | name"}=123;
       | 
       | Isnt it beautiful?
        
         | adrianmonk wrote:
         | Perl, too. (Probably not a coincidence.)
         | 
         | You can also put a newline in a variable name if you really
         | want. Or a 0 byte.
         | 
         | Here's a demo. I've used the debugger because its "X" command
         | can print the true name of the variable:                   $
         | perl -d -e 1              Loading DB routines from perl5db.pl
         | version 1.60         Editor support available.
         | Enter h or 'h h' for help, or 'man perldebug' for more help.
         | main::(-e:1):       1           DB<1> ${"variable-name"} = 123;
         | DB<2> ${"variable\nname"} = 456;                DB<3>
         | ${"variable\0name"} = 789;                DB<4> X ~variable
         | $variable^@name = 789         $variable^Jname = 456
         | $variable-name = 123
        
         | contravariant wrote:
         | So does python I suppose                   locals()["kebab-
         | case"]=123
        
         | oconnor663 wrote:
         | How many languages support kebab case with any Unicode dash
         | that isn't the ASCII one? :)
        
           | jfoutz wrote:
           | At the moment, the only one I know for sure is Agda.
           | 
           | I suspect java would work as well, not sure about golang
           | unicode var naming.
        
             | zimpenfish wrote:
             | play.golang wouldn't let me use figure (-, U+2012), endash
             | (-, U+2013) or emdash (--, U+2014) in a variable name
             | directly.
             | 
             | But then the spec[1] says that only code points
             | characterised as "Letter", an underscore, or characterised
             | as "Number, decimal digit" are valid.
             | 
             | [1] https://go.dev/ref/spec#Identifiers
        
               | leipert wrote:
               | Coptic Small Letter Dialect-P Ni should work, looks like
               | a hyphen.
               | 
               | Go Playground: https://go.dev/play/p/kxgOcEWsznz
               | 
               | https://www.compart.com/en/unicode/U+2CBB
        
           | dunham wrote:
           | Agda does, but it also supports a plain ascii hyphen in
           | identifiers. It allows operator characters inside identifiers
           | and requires spaces around operators otherwise (as proposed
           | in the article). So you can use x-y as an identifier:
           | x-y : Z - Z - Z         x-y x y = x - y
           | 
           | The Agda community also heavily uses unicode characters. I've
           | even seen a unicode colon used for a custom syntax because
           | the ascii colon was unavailable.
        
           | Smaug123 wrote:
           | F# allows arbitrary names within double-backtick identifiers.
           | The following is a valid declaration, where I've used a
           | hyphen, en-dash, and em-dash:                   let ``foo-
           | bar-baz--quux`` = 3
        
           | adastra22 wrote:
           | Probably any language that supports Unicode identifiers.
        
       | prpl wrote:
       | I wrote a small DSL for easy compilation to SQL that included
       | some similar features. It included datetime literals, but they
       | were specified as just a string prefixed with "d".
       | d'2020-02-20'
       | 
       | I also tried to make it so that every comparison had both english
       | and symbol representations, a range syntax, and an
       | approximation/match comparison (e.g. "=~", "!~") which could work
       | with both floating point numbers and strings properly.
       | 
       | I found this useful, and wish it was in more languages.
        
       | [deleted]
        
       | jb1991 wrote:
       | I would add named function parameters to this list. So useful.
        
       | mhd wrote:
       | What I'd like to have brought back is basically the extended
       | version of the numbers/kebap-case: Ignore white space in
       | constants and identifiers if possible, like Algol-68 did.
       | 
       | That means you can write your number as "1 000 000" and put it in
       | the variable "one million", and then feed that to your function
       | called "withdraw money".
       | 
       | Yes, sure, makes it harder to grep. Here's a nickel, get a better
       | grep tool (or wrapper thereof).
        
       | andix wrote:
       | local const variables like JavaScript has them. You can declare
       | variables with ,,let" (mutable) or ,,const" (immutable). This is
       | really great when reading code, because you never have to check
       | if some code may change the variable at some point. And you
       | usually declare most variables as const.
       | 
       | A lot of languages provide immutable variables only for class
       | members or statics, but not for local variables.
        
         | kaba0 wrote:
         | I really like Scala's `val` vs `var`.
        
           | andix wrote:
           | Okay, that would confuse me. Looks very similar and easy to
           | overlook.
        
       | krapp wrote:
       | Omitting parens in a function signature when calling with one or
       | no arguments.
        
         | dragonwriter wrote:
         | This means you can't reference the function itself by name
         | without some kind of quoting construct or other circumlocution;
         | also, while the no-arg case might make sense as a special case,
         | if you allow the one arg case, you might as well admit the
         | general case.
        
       | ljm wrote:
       | Ruby's symbols are undersold in terms of how useful they are and
       | how they enable meta programming and DSL development. You can
       | basically invent new language keywords with them.
       | private def foo           "bar"         end
       | 
       | In this case, 'def foo ... end' returns ':foo', and 'private' is
       | just another method that takes it as an argument and decorates
       | the provided method. It's not a special language keyword.
        
       | ufo wrote:
       | Lua also allows you to choose the string delimiter. If your
       | string contains "]]" you can delimit it with [=[ or [==[ instead.
       | Any number of "=" so long as the opening and closing delimiters
       | match.
        
         | unnouinceput wrote:
         | And that's why all modern languages implement streams/string
         | helpers/string builders. You do not want to actually write
         | strings/manipulate them using "+" (concatenation symbol) in
         | code directly because, in modern Unicode world, it tends to
         | become a point of failure for obscure bugs / a maintenance
         | horror show.
        
           | jbverschoor wrote:
           | I don't understand the link with parent, nor why + would be
           | bad, besides a) because language is naive about concatenation
           | / allocation, and b) if the language allows / doesn't
           | differentiate between a char 'x' and an integer, bc that
           | would result in an integer addition instead of a
           | concatenation.
        
       | jamincan wrote:
       | For rust, it is probably the try (?) operator. Fundamentally,
       | it's just syntax sugar for a match statement with an early return
       | in the Error or None cases, but it really improves the ergonomics
       | of dealing with Result and Option types.
        
       | mncharity wrote:
       | > roughly three classes of language features [...] 3. Quality-of-
       | life features that aren't too hard to add
       | 
       | I'd regrettably add another class, quality-of-life features which
       | you'd have hoped weren't too hard to add, but because of past
       | choices, now are.
       | 
       | Examples: Adding javascript-like dots a.b.c for Julia Dict's
       | a[:b][:c] would conflict with "wasn't intended to be public but
       | has been" Dict implementation fields, like .count . Adding { a,b
       | | ... } instead of a less concise { |a,b| ...} for Ruby blocks,
       | but for a yacc grammar conflict.
        
       | keybored wrote:
       | Agree with kebab-case.
       | 
       | > Most languages have multiline literals, but what makes the Lua
       | version great is that the beginning and ending marks are
       | different characters. This solves the infuriating "unnestable
       | quotes" problem string literals have, and you don't have to
       | escape all your literal \s.
       | 
       | That paragraph also uses "nestable marks".
        
       | turtledragonfly wrote:
       | With regard to strings, they give a good example in Lua, but oh
       | boy wait until this person hears about Perl (:
       | 
       | There's a whole section in the manual [1] for string quoting
       | operators (qq, qw, qx, ...)
       | 
       | In general, I feel like Perl is one of those languages that has a
       | high amount of these "quality of life" syntactic features, and
       | helps make it enjoyable to write, once you get over the learning
       | curve.
       | 
       | [1] https://perldoc.perl.org/perlop#Quote-Like-Operators
        
         | mncharity wrote:
         | Raku (once Perl6) generalizes quoting as a Q , followed by
         | optional "how should this behave" adverbs, and text bracketed
         | by anyish unicode bracket pair. So Q:w <foo bar> is a list of
         | two words. And has Perl-like qw/foo bar/ as sugar. Heredocs are
         | Q:to/THEEND/ ... \nTHEEND . I'm unclear on whether you extend
         | this without defining your own Q-like thing?
         | 
         | Julia allows[2] defining your own non-standard string literals.
         | foo"bar"hee and qux`...` desugar as macro calls
         | foo_str("bar","hee") and bar_cmd("..."). But lack the bracket
         | flexibility.
         | 
         | http://rigaux.org/language-study/syntax-across-languages.htm...
         | briefly sketches other languages.
         | 
         | [1] https://docs.raku.org/language/quoting [2]
         | https://docs.julialang.org/en/v1/manual/metaprogramming/#met...
        
         | hbrn wrote:
         | qw was great. Even in Python I sometimes write:
         | usernames = '''         foo bar baz         hello world
         | '''.split()            # instead of this, which needs too many
         | keystrokes       usernames = ["foo", "bar", "baz", "hello",
         | "world"]
         | 
         | Interestingly, Python named tuples have similar interface for
         | fields:                 # all of these are equivalent
         | EmployeeRecord = namedtuple('EmployeeRecord', ['name', 'age',
         | 'title'])       EmployeeRecord = namedtuple('EmployeeRecord',
         | 'name, age, title')       EmployeeRecord =
         | namedtuple('EmployeeRecord', 'name age title')
        
         | dariusj18 wrote:
         | That's actually a great point. Perl has so many syntactic
         | sugars that it is a poster child for too much variety in ways
         | you can write things. Making it much harder to read someone
         | else's code.
        
           | masklinn wrote:
           | Perl has so much syntactic sugar it's the poster child for
           | syntactic diabetes, really.
        
       | duped wrote:
       | > Second, what if parameter blocks were abstractable?
       | 
       | Sounds like a great way to make unreadable code
       | #define STANDARD_EXP_PARAMS ...              // I hit gotodef on
       | func(...) and got here. What are the arguments?         void func
       | (STANDARD_EXP_PARAMS) {            // ... long function body ...
       | x = y; // What is the type of x? Is it an argument or a local?
       | }
       | 
       | I'd really be irritated if someone ever used this feature,
       | optimizing for lines written is shaky territory to begin with.
       | When it's at an interface boundary it's not excusable. NB4 "use
       | an IDE" - requiring an IDE to make code legible is dumb, and I'm
       | an IDE shill!
        
         | robertlagrant wrote:
         | You could say the same about a function call or a function that
         | takes an object as an argument - who knows what code lies
         | behind the impenetrable barrier of structured or object
         | oriented programming?
        
       | masklinn wrote:
       | > If you look at something like numpy functions, so many of them
       | share the exact same parameter definitions. What if you could
       | write def log(standard-exp-params) instead of having to write
       | them out every single time?
       | 
       | They're not actually written out every time, the issue is mostly
       | documentary (and it would be nice if Python or Sphinx ever had a
       | good solution). And numpy actually has a bunch of generators for
       | that e.g.
       | https://github.com/numpy/numpy/blob/45bc13e6d922690eea43b9d8...
       | handles filling in the common bits of documentation for the
       | ufuncs.
        
         | PyWoody wrote:
         | I don't use Numpy but it sounds like they're describing *, **
         | operators.                   default_args = ('x', 'y', 'z')
         | default_kwargs = {'p': 'p', 'q': 'q', 'r': 'r'}
         | def printer(x, y, z, /, *, p, q, r):          print(f'x={x} |
         | y={y} | z={z} | p={p} | q={q} | r={r}')
         | printer(*default_args, **default_kwargs)  # x=x | y=y | z=z |
         | p=p | q=q | r=r
         | 
         | EDIT: Formatting.
        
           | masklinn wrote:
           | Yes but also that lacks most of the documentation so it's not
           | great.
           | 
           | If you have multiple callables taking these parameters
           | documenting them is awkward, by default help/pydoc and sphinx
           | will tell you that the parameters are `default_args` and
           | `default_kwargs`, but that's not actually true, those are
           | just intended as shortcuts / helpers .
        
       | davidhyde wrote:
       | > You can write # 2001-08-12 # to mean the date 2001-08-12,
       | instead of writing something annoying like Date(2001, 8, 12)
       | 
       | I like this article but oh man dates just trigger me. Such a
       | missed opportunity to use an unambiguous date example like
       | 2001-08-13
        
         | iwsk wrote:
         | It would have been unambiguous in a world where we all agreed
         | that both months and days start at 0 :)
        
           | unnouinceput wrote:
           | Also don't forget about year 0 (which doesn't exists and is a
           | single point of failure for so many programs that deal with
           | calculating time between now and a BC date)
        
         | jakeinspace wrote:
         | Year-day-month would be a truly cursed assumption for a format
        
       | seanalltogether wrote:
       | I really like swift's simple way of collapsing try catch blocks
       | down to a simple value|nil result.                   let data =
       | try? aFuncThatThrows()
       | 
       | Sometimes I just don't care about the reason for the exception
       | and just want to know if it succeeded or not
        
       | pishpash wrote:
       | Great, introduce slang to languages, what could go wrong?
        
         | jedisct1 wrote:
         | Slang itself is a language: https://www.jedsoft.org/slang/
        
       | [deleted]
        
       | ivan_gammel wrote:
       | More sugar for reflection:                 class Foo {
       | @Max(10) int bar;         @NonNull String name();       }
       | var field = @Foo::bar;       var max = @Foo::bar.Max;       var
       | method = @Foo::name;       var foo = new Foo();       var name =
       | method(foo);       var bar = foo.field;
        
       | btown wrote:
       | > [In Chapel] there's the config keyword. If you write config var
       | n=1, the compiler will automatically add a --n flag to the
       | binary. As someone who 1) loves having configurable program, and
       | 2) hates wrangling CLI libraries, a quick-and-dirty way to add
       | single-variable flags seems like an obvious win. Letting people
       | define configurable variables at their call site is incredibly
       | valuable, even if you don't have compile-time support, and even
       | if you're working on something not meant to be an isolated
       | binary.
       | 
       | At my startup, one our most beloved innovations is that you can
       | write `resolve_config("foo", default="bar", request=request)`
       | pretty much anywhere you'd normally hardcode a value or feature
       | flag... and that's it.
       | 
       | The first time it's seen in any environment, it thread-safely
       | inserts-if-not-present the default value into a key-value storage
       | that's periodically replicated into in-memory dictionaries that
       | live on each of our app servers. Any subsequent time it's
       | accessed, it's a synchronous key-value lookup in memory, with
       | barely any overhead. But we can also configure it in a UI without
       | needing a code redeploy, and have feature flags and overrides set
       | on a per-user or per-tenant basis.
       | 
       | Sometimes, you don't need language support if you have some
       | clever distributed-systems thinking :)
        
         | masklinn wrote:
         | > The first time it's seen in any environment, it thread-safely
         | inserts-if-not-present the default value into a key-value
         | storage
         | 
         | That seems like a great way to get amazingly hard to replicate
         | bugs or odd behaviours if different subsystems use different
         | values for the default.
        
           | elcritch wrote:
           | You'd just need to have a mutex lock on the values. That
           | could be slow, but you probably don't want to have a config
           | flag in a hot loop anyways. :)
           | 
           | In Nim you can do compile flags which let you set constants
           | so you avoid the problem:                   const
           | myLibraryVersion {.intdefine.} = 3
        
             | masklinn wrote:
             | > You'd just need to have a mutex lock on the values.
             | 
             | Oh no they're saying that it's thread-safe, that's not an
             | issue. Rather that depending on the order of
             | initialisation, possibly of different systems entirely, you
             | can have different initial states because different systems
             | or subsystem decided of the default value.
        
       | happyrock wrote:
       | I have long wondered why Ruby's symbols aren't in every language.
        
         | RodgerTheGreat wrote:
         | They exist in K/Q. A single-word identifier-shaped symbol
         | begins with a backtick, or a multi-word symbol can be created
         | with a backtick and double quotes. A sequence of symbols is a
         | vector literal, and is stored compactly. For example:
         | `apple         `"cherry pie"         `one`two`three
         | 
         | Many languages will intern string literals implicitly, or allow
         | a programmer to explicitly intern a string; for example Java's
         | "String.intern()".
         | 
         | The problem with string interning, especially for strings
         | constructed at runtime, is that for the interning pool to be
         | efficient it is very desirable for it to be append-only, and
         | non-relocatable. A long-running program which generates new
         | interned strings on the fly risks exhausting this pool or
         | system memory.
        
           | masklinn wrote:
           | > A long-running program which generates new interned strings
           | on the fly risks exhausting this pool or system memory.
           | 
           | So does a long-running program which generates new symbols on
           | the fly.
        
         | masklinn wrote:
         | Because in most languages they're not useful. Symbols are
         | solutions to problems, some of which are:
         | 
         | 1. mutable strings (ruby)
         | 
         | 2. and / or expensive strings (erlang, also non-global)
         | 
         | If you have immutable "dense" strings and interning, and you
         | automatically intern program symbols (identifiers, string
         | literals, etc...) then symbols give you very little.
         | 
         | And then there's the slightly brain damaged like javascript,
         | where symbols are basically a way to get some level of
         | namespacing to work around the dark years of ubiquitous ad-hoc
         | expansions so you're completely stuck unable to add new program
         | symbols to existing types because you could break any page out
         | there doing something stupid.
        
         | amalgamated_inc wrote:
         | Both Lisp and Erlang have them, so they're much older than
         | Ruby.
        
         | revskill wrote:
         | Is it for performant reason ?
        
           | onei wrote:
           | Symbols in Ruby are meant to be more performant that strings
           | iirc. If I have symbol :a, then it's allocated once
           | regardless of how many time it appears. As opposed to "a"
           | which is reallocated every time.
           | 
           | I guess it's similar to Python having a single instance of
           | small integers. PlayStation also experimented with caching
           | small floats which gave them some perf improvements too, but
           | I think wasn't as performant in all cases.
        
             | justincormack wrote:
             | Lua (and some other languages) intern strings, so all
             | strings that are the same point to the same string
             | instance. This gives the same benefits (plus string
             | equality is just pointer equality) without a different
             | type.
        
             | pawelduda wrote:
             | There is a caveat in older Ruby versions that they aren't
             | garbage collected, so they shouldn't be used for things
             | like user input. Not a problem since 2.2 though.
        
           | progval wrote:
           | Symbols can even improve performance. Replace them with
           | integers at compile time like a global enum, and so the
           | runtime only needs to compare integers instead of potentially
           | lengthy (especially if UTF-16) strings.
        
             | masklinn wrote:
             | > Replace them with integers at compile time like a global
             | enum, and so the runtime only needs to compare integers
             | instead of potentially lengthy (especially if UTF-16)
             | strings.
             | 
             | All of those strings will be interned, and can thus be
             | compared by identity. Which is an integer comparison.
        
       | AnthonBerg wrote:
       | kebab-case
       | 
       | enkebab-case
       | 
       | emkebab--case
       | 
       | These use dash, en dash and em dash, respectively. Most languages
       | that allow you to use a decent amount of Unicode in variable
       | names probably will accept that kind of kebab-case.
       | 
       | Those dashes look pretty similar to each other in monospaced
       | fonts but not indistinguishable, so it's readable and not super
       | confusing. Might work. Why not?
        
       | surfsvammel wrote:
       | I love all the sugar of Kotlin, but there is one thing I miss,
       | and it is the indexing of arrays of Python when pulling out part
       | of an array.
        
         | amalgamated_inc wrote:
         | Kotlin is so close on so many things, but then keeps messing
         | up.
         | 
         | arrayOf(1, 2, 3) - why not [1,2,3]?
         | 
         | emptyArray() - why not []?
         | 
         | mapOf("key" to "value") - why not {key => value}?
         | 
         | These are all solved problems. Why would they make up some
         | harshly suboptimal syntax?
        
           | kaba0 wrote:
           | I am not that familiar with Kotlin, but these seems better
           | than the syntax primitives from a language design perspective
           | (I greatly recommend the "Growing a language" presentation
           | done by Guy Steele), these are ordinary functions that are
           | well-known from other parts of the language, not an added
           | "hack" that has a one-off use. If you were to use a
           | concurrent hashmap implementation you no no longer can use
           | the syntactic sugar, and writing against an implementation is
           | quite common in Java (which plays quite a big role in the
           | design of Kotlin), e.g. having a List in the interface,
           | instead of ArrayList.
        
             | amalgamated_inc wrote:
             | Interesting, I think the exact opposite :)
        
       | rwmj wrote:
       | Perl's if and unless operators, which can also be postfixed, eg:
       | die "can't be negative" unless $i >= 0;
       | 
       | Perl is full of usability features, like <> for reading input,
       | inline literate coding annotations, implicit $_.
        
       | jedisct1 wrote:
       | Comptime.
       | 
       | After having discovered Zig, I've been missing that feature in
       | every other language.
        
         | amalgamated_inc wrote:
         | Compare to lisp macros?
        
         | AnIdiotOnTheNet wrote:
         | And error unions with corresponding semantics. And explicit
         | casting requirements. And @TypeInfo. And no hidden allocations.
         | And probably like 4 or 5 other things I'm not thinking of right
         | now.
        
         | Deukhoofd wrote:
         | Comptime is great, but I wouldn't classify it as a
         | microfeature. It'd fall more in the second category the author
         | defines.
        
         | [deleted]
        
         | gavinhoward wrote:
         | For me, yes and no.
         | 
         | Sure, comptime is great, but I've also found it hard to reason
         | about code with it. I prefer my comptime stuff separated out
         | into its own section/file/whatever. With that small change, it
         | becomes _so_ much easier.
         | 
         | But yeah, still powerful and nice.
        
       | lullab wrote:
       | I'd like to see the "in" operator from SQL in C-style languages.
       | 
       | Something like:                 if(x in (null, 2, 3.14, foo(123))
       | {         //       }
        
       | duped wrote:
       | Python's `with`
       | 
       | Function composition operators eg                   a |> b # Call
       | `b` with `a` as an argument         b <| a # Same as above,
       | reversed direction
       | 
       | Then you can do something like                   let x : Map =
       | collect <|           [a, b, c]             |> map(entries)
       | |> flatten
        
         | masklinn wrote:
         | > Python's `with`
         | 
         | It's very common already: try-with-resource (java), using (C#),
         | bracket (haskell), unwind-protect (common-lisp), ... though it
         | the latter two it's more of a building block.
         | 
         | Also building block: languages with a convenient and
         | "unrestricted" syntax for anonymous function can just use that
         | e.g. Smalltalk, Ruby, ... in Ruby a "with" is usually just
         | passing a block to the corresponding object's constructor:
         | # python          with open(...) as f:             ...
         | # ruby         File::open(...) do |f|           ...         end
        
         | jbverschoor wrote:
         | I was under the impression that Elixir introduced this.
        
           | masklinn wrote:
           | Elixir has forward pipes, but didn't invent them.
           | 
           | For instance Racket and Clojure have _threading macros_ ,
           | which are more flexible as they're just macros (Clojure's
           | `->` is equivalent to Elixir's pipe operator, but `->>` will
           | fill in the _last_ parameter rather than first, and `-- >`
           | lets you use a keyword to define where the parameter is
           | inserted in each call).
           | 
           | Haskell let anyone who wants define their own pipe operator,
           | historically you had to BYO, which wasn't exactly hard:
           | (|>) = flip ($)
           | 
           | or                    x |> f = f x
           | 
           | would do (modulo fixity), but today it's provided by default
           | as "(&)".
        
         | whatshisface wrote:
         | I don't get it, how are |> and <| any different from
         | parentheses?
        
           | mikepurvis wrote:
           | Chaining without nesting.
        
             | whatshisface wrote:
             | If you mean without closing parentheses, I think you can
             | also do that in languages like Haskell with non-
             | parenthetical function calls.
        
               | mikepurvis wrote:
               | I'm not a Haskell user, but my experience with this in
               | the Nix language is a bit mixed. It definitely works
               | _sometimes_ , but then you get a pileup of parenthesis
               | nesting anyway, because the default is greedy and you
               | have to control which functions get which arguments.
        
       | andix wrote:
       | I want more implicit typing in typed languages. Quite often the
       | compiler knows exactly what type a function will return, but I
       | still need to write it there. Sometimes it's easy (,,int"), but
       | what about HashSet<Immutable<Tuple<int,string>>>
       | 
       | Typescript does it well. F# (completely statically typed) too...
        
         | kaba0 wrote:
         | Nowadays even Java will do that, var list =
         | yourFunctionReturningThatSet();
        
           | andix wrote:
           | Also on a function?                 var getDate() { return
           | ,,no date"; }
        
             | masklinn wrote:
             | That is often considered undesirable because it makes code
             | less clear and compilation errors inscrutable.
             | 
             | For instance the rust developers consciously decided to
             | remove that from the language, named functions must be
             | fully typed.
        
             | ivan_gammel wrote:
             | No, but I think that was a good choice. In local code this
             | is great, but in an interface contract being explicit about
             | data type is a virtue.
        
               | andix wrote:
               | For interfaces this would obviously not work (there is no
               | implementation). But for class members it would.
               | 
               | I think this concept works very well in typescript and
               | F#.
        
         | mikepurvis wrote:
         | Rust does a pretty decent job of this, particularly around
         | functions that return collect().
         | 
         | But yeah, C++ has a ways to go on type inference.
        
       | pgorczak wrote:
       | Clojure's loop expression hits this spot for me. It sets a
       | recursion point to which you can jump using any logic inside the
       | body you want, as long as it is from tail position. It's like a
       | while loop turned into an expression. I haven't encountered any
       | other way to write iterative expressions whose number of
       | iterations isn't known at the top (like map and reduce).
        
         | japanman425 wrote:
         | for x in y: yield x
         | 
         | Job done
        
           | tremon wrote:
           | Also known as (in Python, that is):                 yield
           | from y
        
             | masklinn wrote:
             | They were providing a partial example, "yield from" does
             | not actually do what the original poster asks about, it
             | merely proxies the inner iterable.
        
         | bogdanoff_2 wrote:
         | Is that like using "continue" in most C-syntax languages?
        
         | wging wrote:
         | Tail call optimization can get you that, too. If you've written
         | Scheme and/or gone through SICP you might be familiar with
         | this: you write a recursive function, with the recursive
         | function call as the last thing the function does ('tail-
         | recursion'), and the compiler/runtime is able to optimize those
         | recursive calls out rather than consuming one stack frame of
         | space per call ('tail call optimization'). Clojure has
         | loop/recur at least partially because it _doesn 't_ support
         | tail-call optimization.
         | 
         | See https://en.wikipedia.org/wiki/Tail_call for more. Or SICP
         | might be a good resource.
         | https://sarabander.github.io/sicp/html/1_002e2.xhtml
        
           | dragonwriter wrote:
           | TCO also, unlike special syntax for direct tail recursion,
           | works when the last call is _not_ (directly) recursive (which
           | supports indirect /mutual recursion, and just structures with
           | deep call heirarchies that aren't necessarily recursive.)
        
           | amalgamated_inc wrote:
           | Interestingly, I almost prefer Clojure's `recur`
           | semantically. Means you don't have to change the function
           | name twice if you rename it, and it's hard to miss that
           | you're recursing.
        
             | wging wrote:
             | Those _are_ cool properties. Another one is that you get a
             | compilation error if your recursive call isn 't in the tail
             | position (and thus would actually grow the stack when you
             | thought it didn't).
             | 
             | One thing I don't think you can do with loop/recur, though,
             | is optimize more complicated bits of recursion than a
             | single function that calls itself. I.e. imagine a recursive
             | call pattern that goes like f -> g -> f -> g -> ...
             | 
             | (edit: I'm pretty sure this is why trampoline exists,
             | though I've never really played with it...
             | https://clojuredocs.org/clojure.core/trampoline)
        
             | masklinn wrote:
             | If that's your worry then you can probably use the site's
             | namesake. Though simple recursion is generally easy to
             | spot.
        
               | amalgamated_inc wrote:
               | Which site's namesake? Hacker News?
        
               | masklinn wrote:
               | The Y combinator.
        
               | amalgamated_inc wrote:
               | Oh is this some Common Lisp thing? Never done it.
        
               | masklinn wrote:
               | It's much older, it's lambda calculus stuff. It's a way
               | to implement recursion in a language which doesn't have
               | recursive functions (but for some reason does have first-
               | class functions).
               | 
               | However it allows making anonymous functions recurse as
               | well.
        
             | 613style wrote:
             | It's also nice to get an error when you `recur` from a non-
             | tail position rather than the function just quietly
             | becoming truly recursive.
        
         | masklinn wrote:
         | > I haven't encountered any other way to write iterative
         | expressions whose number of iterations isn't known at the top
         | (like map and reduce).
         | 
         | `unfold`, Rust's `loop`, generators, working tail recursion
         | elimination (the lack of which loop/recur is a workaround for)
        
       | light_hue_1 wrote:
       | Haskell has almost all of these.
       | 
       | > Instead of writing 10000500, you can write 10_000_500, or
       | 1_00_00_500
       | 
       | https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/nume...
       | 
       | > Balanced string literals
       | 
       | https://hackage.haskell.org/package/raw-strings-qq-1.1/docs/...
       | 
       | > Generalized update syntax
       | 
       | Use Lens. `fileName %~ max 2`
       | 
       | > you can write the sequence 1, 2, ... n-1 as 1..<n.
       | 
       | Yup. `[1,2..n-1]` There's far more to it, you have access almost
       | a SQL-like sublanguage.
       | 
       | > Symbols
       | 
       | In Haskell you use hash has a prefix instead of colon.
       | 
       | Haskell sadly does not do automatic lifting, no extended
       | parameter blocks, and no kebab-case.
        
         | unhammer wrote:
         | >> Symbols
         | 
         | > In Haskell you use hash has a prefix instead of colon.
         | 
         | Can you give an example?
        
       | purplie wrote:
       | Declaration of units for primitive numeric variables.
       | 
       | Example: <https://github.com/mchrisman/variables-with-units-
       | language-p...>
        
       | xigoi wrote:
       | My favorite is uniform function call syntax. In several languages
       | (Nim, Koka, D, ...), you can _always_ write bar.foo(baz) instead
       | of foo(bar, baz) and vice-versa.
       | 
       | Another one from Nim is the implicit result variable. Instead of
       | having to do this:                   func sum(nums: seq[int]):
       | int =           var result = 0           for num in nums:
       | result += num           return result
       | 
       | you just do this:                   func sum(nums: seq[int]): int
       | =           for num in nums:             result += num
       | 
       | It saves so much time and I'm disappointed that more languages
       | don't have it.
        
         | tremon wrote:
         | _and vice-versa_
         | 
         | Doesn't the reverse pollute the function namespace? If every
         | obj.fun() can be written as fun(obj), doesn't that cause
         | ambiguity with a previously imported global function fun()?
        
           | __jem wrote:
           | In Rust, it's still qualified by the type you're calling it
           | on, which I assume is also the case in other languages.
        
             | masklinn wrote:
             | > which I assume is also the case in other languages.
             | 
             | Nope. Rust has an extremely restrictive form of UFCS. In
             | fact officially it's _not_ called UFCS, but  "Fully
             | Qualified Path syntax".
        
           | Yujf wrote:
           | In rust you can do this but they are namespaced with the name
           | of the struct. So you can do Object::fun(obj)
        
           | lalaithion wrote:
           | If you have two functions abracadabra(foo) and
           | foo.abracadabra() which _do different things_ , you should
           | rename one of those functions tbh.
        
             | feoren wrote:
             | The problem is when you have the functions:
             | 
             | Square.computeArea()
             | 
             | Circle.computeArea()
             | 
             | Clearly these should do different things. I suppose
             | "computeArea(shape)" does dynamic dispatch based on the
             | type of shape? But you're still putting every function
             | defined on every type in your entire codebase in a global
             | namespace. It's not _obviously_ awful but I 'd definitely
             | be a bit nervous about it.
        
           | masklinn wrote:
           | > doesn't that cause ambiguity with a previously imported
           | global function fun()?
           | 
           | Yes, although overloading can mitigate the issue.
        
             | elcritch wrote:
             | Yah I ran into that issue more in Julia. There's a built-in
             | function to help find clashing functions.
             | 
             | In Nim however I've only had it happen a handful of times
             | in a few years. Then you just need to use the module name
             | to qualify it, or change your imports.
        
           | AnimalMuppet wrote:
           | That sounds like no fun() at all.
           | 
           | Right, I'll show myself out...
        
         | andix wrote:
         | I'm not so sure about that. Did you ever check out extension
         | methods in C#, they are a bit like what you're describing, but
         | not so radical.
        
         | barrkel wrote:
         | Implicit result var is also in Delphi, as Result. The original
         | Pascal is to assign to a variable with the same name as the
         | function, which looks kind of odd, and is overloaded in
         | recursive scenarios, since functions with no args don't need
         | parens to invoke.
        
         | onei wrote:
         | I think you can also do the second in Go with named returns,
         | e.g.                   func sum(nums []int) (result int) {
         | for _, n := range nums {                 result += n
         | }         }
         | 
         | No clue if it's an idiomatic usage, and named returns always
         | felt a little too magic for me.
        
           | mikepurvis wrote:
           | Lots of languages will implicitly return the final
           | expression-- I feel like that's a decent compromise. Not
           | quite as magical as an actual named variable that just
           | exists, but not as clunky as needing as explicit `return`
           | every time.
        
           | zimpenfish wrote:
           | It's kinda fine if it's a short function like that[1]. When
           | you get above 10-15 lines in a function though, it's easy to
           | lose track of what's a return variable and what isn't.
           | 
           | [1] _and_ everything in the codebase uses that style
           | otherwise it 's annoying to have to context-switch every 5
           | minutes.
        
         | michaelsbradley wrote:
         | There are some cons to using `result`:
         | 
         | https://status-im.github.io/nim-style-guide/language.result....
        
       | devmunchies wrote:
       | # Function Shorthand
       | 
       | I like how functions in js can be `arg => result`. In F# I have
       | to do `fun arg -> result` with the `fun` keyword. It makes sense
       | since `MyArgType -> MyResType` is a type signature in f#, but I
       | feel like the compiler can just check if the arguments are
       | references to types or are argument bindings.
       | 
       | # Multiline Lists/Arrays
       | 
       | I like how F# doesn't require delimiters for multiline lists.
       | 
       | So I can do `let myList = [1; 2; 3]` or                   let
       | myList = [           1           2           3         ]
       | 
       | # Regex Literal
       | 
       | I like how in Crystal instead of doing `/my[regex]/` i can do
       | `%r(my[regex])` where the parenthesis can be any brace type (like
       | "(", "{", "<", "[") so I don't have to escape any characters.
        
         | dan-robertson wrote:
         | The function syntax I like even more is one with implicit
         | arguments so you don't have to name them, e.g.
         | waiting = sum workers #(%.in_queue + %.in_flight)
         | 
         | Clojure has some syntax like this though it isn't needed for
         | the most obvious use-case of functions to extract fields
         | because keywords, which are usually used for map keys, are
         | implicitly functions that look themselves up in their arg, e.g.
         | (:bar { :foo 3, :bar 2 })  ; => 2
        
         | masklinn wrote:
         | > I feel like the compiler can just check if the arguments are
         | references to types or are argument bindings.
         | 
         | 1. this is absolutely terrible because now you need feedback
         | from the type checker to know how to parse the program
         | 
         | 2. it is furthermore also ambiguous with function _application_
         | , requiring arbitrary lookahead to disambiguate, also not a fun
         | thing to do
         | 
         | JS gets away with it because the sigil was not previously used
         | and it only requires a single lookahead to parse, as only
         | single-parameter anonymous functions can have "bare" parameter
         | lists.
        
       ___________________________________________________________________
       (page generated 2023-01-05 23:01 UTC)