What's a useful programming language feature or concept that a lot of languages don't have?
Dustin King

Dustin King @cathodion

About: Python. Webdev. Music. Also, other stuff.

Joined:
Jan 29, 2017

What's a useful programming language feature or concept that a lot of languages don't have?

Publish Date: Nov 29 '18
37 34

One thing that comes to mind is Ruby's macro-style methods:

class BankAccount < Account
  attr_accessor :balance
  belongs_to :customer
end
Enter fullscreen mode Exit fullscreen mode

What's something your favorite language has that others don't? Or something from a language you tried out that one time and immediately missed it in other languages?

Comments 34 total

  • Ben Halpern
    Ben HalpernNov 29, 2018

    Elm's claim-to-fame type inference is pretty wonderful

    No Runtime Exceptions

    Elm uses type inference to detect corner cases and give friendly hints. For example, what if someone provides invalid inputs? NoRedInk switched to Elm about two years ago, and 250k+ lines later, they still have not had to scramble to fix a confusing runtime exception in production.

    -- TYPE MISMATCH ---------------------------- Main.elm
    
    The 1st argument to `drop` is not what I expect:
    
    8|   List.drop (String.toInt userInput) [1,2,3,4,5,6]
                    ^^^^^^^^^^^^^^^^^^^^^^
    This `toInt` call produces:
    
        Maybe Int
    
    But `drop` needs the 1st argument to be:
    
        Int
    
    Hint: Use Maybe.withDefault to handle possible errors.
    
    Enter fullscreen mode Exit fullscreen mode

    elm-lang.org/blog/compilers-as-ass...

    • Kasey Speakman
      Kasey SpeakmanNov 29, 2018

      The thing not mentioned that is really the key to this equation is that end-user Elm code cannot perform side effects. So all the code you write in Elm is purely calculation. Combined with the ML-family type system, you almost have to do it on purpose to crash Elm. I've found a case or two that will unexpectedly crash it (e.g. using a HTML attribute with a blank name and value), but caught it right away in dev.

      I only mention it because the same type inference is available in other languages (Reason, F#, OCaml). But these languages cannot make the same guarantee since side effects are permitted. This is a pragmatic choice for them, because they are general purpose languages. It is an outstanding wish of mine to be able to mark specific code in F# as side-effect-free and have the compiler enforce that, in case my own discipline slips.

      • Ben Halpern
        Ben HalpernNov 29, 2018

        Yes. If anything this has kept me from adopting Elm for real even though I'm fairly enamored by it.

        I've always sort of relied on the escape hatch you don't get it Elm. I'm much more used to programming in the chaos of Ruby. 🙃

        Some day though. Some day, I'll find the discipline to stick to something like Elm.

        • Kasey Speakman
          Kasey SpeakmanNov 29, 2018

          It seems more limiting than it is in practice. But I can imagine Ports (the JS interop) being a pain if you need to integrate a lot of existing JS libraries. They closed the more direct escape hatch (aka native, kernel) in 0.19. And yeah, I have the sentiment that things are a bit too rigidly prescriptive in Elm-land. But it taught me a lot -- especially the depth of what you can do with just calculation. It's hard to go back to anything else after that, because pure calculations are so refactorable and testable. We are trialing Fable-Elmish, which is basically Elm with opt-in side effects. It is written in F#, which is syntactically close to Elm. I even have a strategy for isolating side effects to be able to keep the rest of the code pure.

          • Avalander
            AvalanderNov 29, 2018

            If that's the case, I would suggest looking into PureScript. I dabbled in Elm a while ago and I really liked it, but I felt that the language had way too many opinions built into it, because it's actually a language and a framework.

            PureScript is very similar to Elm, it's also heavily inspired by Haskell, has type inference, and compiles down to Javascript. However, it doesn't have a strict architecture built in and the interoperability with Javascript is a bit more straightforward.

            I've been toying with PureScript for a couple of weeks now and, honestly, I'm not sure I'll ever go back to Elm. Plus, you can use PureScript for backend code too.

            • Kasey Speakman
              Kasey SpeakmanNov 29, 2018

              Thanks for the suggestion. I’ve been meaning to try it, but it’s philosophical closeness to Haskell gives me pause.

              • Avalander
                AvalanderNov 29, 2018

                Oh, hey! I meant to reply to Ben, but apparently we wrote at the same time and you won 😅

                Still, if you're familiar with Elm it shouldn't be hard to get the hang of it.

  • Rob Hoelz
    Rob HoelzNov 29, 2018

    I don't know if I miss them all the time, but dependent types (found in Idris, among other languages) are very interesting. Learning Idris definitely had an influence on how I think about certain problems; for example, at work I needed to write some code to partition items in a user's cart into separate shipments. A post-condition of this code is that the total number of items across all of the returned shipments should be the same as the total number of items in the user's cart; in most languages, you would use rigorous testing to assert you'd done this, but in Idris, you can express this property in the type system itself!

    Something I miss more often (also found in Idris, along with other ML-family languages) is type-directed search. If you don't know the name of a function, but you can describe what it does in a type, you can use that type to find the function you need. For example, let's say we don't know how to append vectors in Idris (vectors are like lists in other functional languages, only their length is part of their type). You can think of the type of append as being "take a vector of length N containing items of type A, and a vector of length M containing items of type A, and return a vector of length (N + M) containing items of type A". So we ask Idris to find such a function for us:

    *Data/Vect> :search Vect n a -> Vect m a -> Vect (n + m) a
    

    ...and it gives us the answer!

    *Data/Vect> :search Vect n a -> Vect m a -> Vect (n + m) a
    = Data.Vect.(++) : Vect m elem -> Vect n elem -> Vect (m + n) elem
    Append two vectors
    
    > Data.Vect.merge : Ord elem => Vect n elem -> Vect m elem -> Vect (n + m) elem
    

    Smalltalk has something similar in its method finder; we can ask it "which messages can I send to 'foo' that return 'FOO', and it'll tell us asUppercase will do this for us.

    A random wacky feature in Scheme that I can't say I think about a lot (other than being just plain interesting and mind-bending) is continuations; they could merit their own post, but continuations are a way of encapsulating the "next step" a program would take. I recommend reading the Wikipedia article if that piqued your curiosity!

  • Kasey Speakman
    Kasey SpeakmanNov 29, 2018

    Two things I think are really powerful in F# that are not found in most languages.

    Union types are a way to represent one of multiple different possibilities. It is a bit like an enum type that can have extra data with it.

    type Payment =
        | ChargeCard of issuer:string * pmtToken:string
        | Check of checkNumber:int
        | Cash
    
    let payment = Check 12345
    

    Most languages have a way you can kinda-sorta define these. One common method in OO is an abstract base class with shallow inheritance. But it is a lot of code overhead versus the concise definition above. And it is also not particularly idiomatic to OO. People probably wouldn't bat an eye at using case on an enum, but type checking for a derived class is often considered a code smell.

    You can also represent these choices as nullable fields in a single class, but then you have to write a bit of code to control access and protect against the case where none or multiple of them are set at once. Doable, but annoying enough that people usually don't.

    Value equality by default is very nice for testing. It means I can do things like this:

    type SomeType = {
        FooInt : int
        BarString : string
    }
    
    let expected = { FooInt = 1; BarString = "One" }
    ...
    let actual = runSomeFunction input
    actual = expected
    

    If the actual has the same values as expected (1 and "One" here), then it will return true. And it works for union types too. In most languages, these things have reference equality by default. So the last line would only return true if actual and expected were pointing to the same memory location, which is not super helpful. You can usually get value equality in any language, but it might be a pain. In C# for example, you can override the default implementations of GetHashCode and Equals to get value equality. It adds an extra 8+ lines of code to every class definition (and each field adds more lines of code potentially). I believe a lot of people use tools to add them in automatically and then code folding to hide them. Or just manually check for equality when needed.

    And I could go on: no nulls, immutability by default, pattern matching, composable asyncs, sequences, etc. Then I need to go fix something in our VB code base, and it's 😬😬😬. Well, one thing VB has that most others don't is XML literals. So there's that.

    • Jan van Brügge
      Jan van BrüggeNov 29, 2018

      I don't like when languages call abstract data types "union". Because it is technically not true. They are disjoint unions, which makes them nice to use. The only language that I know that has actual union types is typescript.

      • Kasey Speakman
        Kasey SpeakmanNov 29, 2018

        F# calls them discriminated unions, technically. For the purposes of my comment I called them union types to keep it conceptually simple. The extra adjective "disjoint" or "discriminated" is more technically correct, but for many people it automatically biases them to react with "too complex; didn't read". When really it is a simple concept.

  • Brian Kephart
    Brian KephartNov 29, 2018

    String interpolation.

  • Arne Mertz
    Arne MertzNov 29, 2018

    Deterministic object destruction is probably my favorite feature of C++ that is found in only a select few other languages. When execution leaves a function or any other scope, be it regularly or through an exception, all local objects get destroyed and their destructors executed. Not eventually when a garbage connector feels like cleaning up some leftovers but immediately. This includes the automatic destruction of that class' member objects.
    That means there's also no need for finally: cleaning up all those file handles, memory allocations and network sockets and painting the sky blue again is done automagically by the destructors of properly written filestreams, smart pointers, socket classes and temporary sky blackeners.

  • Avalander
    AvalanderNov 29, 2018

    Pattern matching as done in ML languages.

    factorial :: Int -> Int
    factorial 0 = 0
    factorial 1 = 1
    factorial n = n * (factorial (n - 1))
    
    Enter fullscreen mode Exit fullscreen mode

    I think it's a much more elegant way to handle end conditions and special cases than if statements and it makes the code much easier to reason about.

    It is also awesome when used in conjunction with union types.

    data Pony = Pegasus String Int
      | Unicorn String String
      | EarthPony String
    
    applejack = EarthPony "Applejack"
    twilight = Unicorn "Twilight Sparkle" "levitation"
    rainbow = Pegasus "Rainbow Dash" 20
    
    
    f :: Pony -> String
    f (Pegasus name speed) = name <> " flies at " <> (show speed) <> "mph"
    f (Unicorn "Twilight Sparkle" _) = "Twilight Sparkle is an Alicorn now, get over it!"
    f (Unicorn name spell) = name <> " casts " <> spell
    f (EarthPony name) = name <> " is a nice pony"
    
    Enter fullscreen mode Exit fullscreen mode

    If I had ever understood lisp's macros I would probably mention them here too.

    • rhymes
      rhymesNov 29, 2018

      Pattern matching, really loved it when I came across it in Erlang. Should brush up on declarative languages

  • Ali Spittel
    Ali SpittelNov 29, 2018

    Python's list comprehensions! The syntax is super elegant, and they are really performant.

    In JavaScript, to filter values, you would do something like this:

    arr.filter(function(i) {
      return i > 0
    })
    

    in Python, you would use a list comprehension

    li = [i for i in li if i > 0]
    

    You can also transform all the elements in a list (similar to a map in JavaScript):

    li = [x * 2 for x in li]
    
    • Avalander
      AvalanderNov 30, 2018

      List comprehension is awesome!

  • Philip Hallstrom
    Philip HallstromNov 29, 2018

    Thanks for the nightmarish flashbacks! 😂😱

  • Alper Cugun-Gscheidel
    Alper Cugun-GscheidelNov 29, 2018

    I miss Swift's strongly typed enums with associated values a lot in other languages (Go, Python etc.).

    From the textbook:

    enum Barcode {
        case upc(Int, Int, Int, Int)
        case qrCode(String)
    }
    
  • rhymes
    rhymesNov 29, 2018

    Ahahahaha

  • Jan van Brügge
    Jan van BrüggeNov 29, 2018

    Monads. Seriously! They make composing function so easy: For example you parse a number for a string the user types in (which might fail if the string is not a number), then you want to index a list at that position (which could fail because the list might be shorter) and then you want to print the element at that position out or an error if a previous step failed. In Haskell this looks like this (I added the type signatures of the functions used on top):

    readMaybe :: String -> Maybe Int
    indexMaybe :: [String] -> Int -> Maybe String
    fromMaybe :: String -> Maybe String -> String
    
    myList = ["Hello", "World"]
    
    doStuff :: String -> String
    doStuff = fromMaybe "Error" . (readMaybe >=> indexMaybe myList)
    
    main = const getLine >=> putStrLn . doStuff $ ()
    

    Here you see the same function (>=>, which has type Monad m => (a -> m b) -> (b -> m c) -> (a -> m c) btw, so it just composes two monadic functions) used for two completely different contexts: First for the Maybe type and then for doing IO. This is the beauty of Monads (and similar type classes). They provide a unified interface over a great range of types. Once you know how to use Monads in general, you can use it on any type that is a Monad. No more consulting the specific API of the library. Everything has the same API!

  • TK
    TKNov 30, 2018

    In Clojure, I really like the threading macro.

    Imagine we have a string " I will be a url slug ", and we want to transform it into a valid "url". Let's build a slugify function to do this transformation.

    (defn slugify
      [string]
      (clojure.string/replace
        (clojure.string/lower-case
          (clojure.string/trim string)) #" " "-"))
    
    (slugify " I will be a url slug   ") ;; "i-will-be-a-url-slug"
    
    Enter fullscreen mode Exit fullscreen mode

    Here we have:

    • trim: removes whitespace from both ends of string
    • lower-case: converts string to all lower-case
    • replace: replaces all instance of match with replacement in a given string

    Another solution is to use the threading macro. Basically we can compose functions using the -> operator.

    (defn slugify
      [string]
      (-> string
          (clojure.string/trim)
          (clojure.string/lower-case)
          (clojure.string/replace #" " "-")))
    
    (slugify " I will be a url slug   ") ;; "i-will-be-a-url-slug"
    
    Enter fullscreen mode Exit fullscreen mode

    Beautiful!

    PS. another solution is to compose all function using comp function :)
    PS2. I wrote a post about Functional Programming with Clojure

    • Dustin King
      Dustin KingNov 30, 2018

      It's nice not having to work backwards:

      "Take the string, then do x to it, then do y to it"

      instead of

      "Take the result doing z to the result of doing y to the result of doing x to the string".

      I once wrote something to let you do this in Python:

      def incd(x, by=1):
          return x + by
      
      print('7 ==', fv(1).incd().incd(2).incd(by=3))
      #=>  7 == 7
      
      Enter fullscreen mode Exit fullscreen mode

      Python had the idea that methods are just functions (hence having an explicit self parameter), it just didn't take it far enough.

  • Juan Julián Merelo Guervós
    Juan Julián Merelo GuervósNov 30, 2018

    Perl 6 grammars
    They go many steps further than simple regexes, organizing and systematizing them. Plus they are classes than can use roles, can be subclassed, or have class variables. Here's an article I wrote about them.

  • Will F
    Will FNov 30, 2018

    pointers - C
    templates - C++
    decorator syntax - Python
    pipes - bash

  • Tobias SN
    Tobias SNNov 30, 2018

    Switches and cases. I love them. But when I tried out Python, I was really surprised that it didn’t have them, especially since the C# syntax looks a lot like it could be Python. For example, the Python equivelant of

    switch(word):
        case(Hello):
            otherWord = World;
            break;
        case(World):
            otherWord = Hello;
            break;
        default:
            otherWord = FooBar;
            break;
    

    is currently

    if word == Hello:
        other_word = World
    elif word == World:
        other_word = Hello
    else:
        other_word = FooBar
    

    I know that both PEP 275 and PEP 3103 have been rejected, but I have high hopes that a new one is created and accepted.

    • Dustin King
      Dustin KingDec 1, 2018

      Maybe it has something to do with having "one way to do it". I never understood the hate for switch/case statements though. It's a little counter-intuitive how they fall through by default in C/C#/C++, but it's still a common thing to want to map a set of possible values to a set of possible actions.

      Or return values! Like in Ruby:

          case serial_code
          when /\AC/
            "Low risk"
          when /\AL/
            "Medium risk"
          when /\AX/
            "High risk"
          else
            "Unknown risk"
          end
      

      There's another thing I like from Ruby and Lisp: you don't have to explicitly return, you can just have the return value be whatever the last expression was.

      • Tobias SN
        Tobias SNDec 1, 2018

        Yeah you’re probably right.

    • Moinuddin Quadri
      Moinuddin QuadriDec 2, 2018

      If you are doing simple task which involves just one statement, instead of using if-elif in Python, you may implement switch statements more elegantly using dictionary.

      For example:

      def switch(key):
           return {
                'a': 'value_a',
                'b': 'value_b'
          }.get(key, 'value_default')
      

      Sample Run:

      >>> switch('a')    # 'a' exists as key
      'value_a'
      >>> switch('z')    # 'z' doesn't exists as key
      'value_default'
      

      Above function will return "value" function corresponding to "key". If "key" not found in dictionary, it will return value as "default_value", just like default in switch statements.

      • Tobias SN
        Tobias SNDec 2, 2018

        That's not really a switch statement, but more of a dictionary abstraction. In a real switch, you can run code based on the value. If I were to modify your snippet, I'd have to get busy with functions and stuff.

  • Paul Lefebvre
    Paul LefebvreNov 30, 2018

    I think this is more of an underutilized features rather than an uncommon one, but I really like Extension Methods. They allow you to "attach" a method to a class that you otherwise can't or don't want to modify.

    For example, rather than:

    Foo(FrameworkClass)
    

    You can write:

     FrameworkClass.Foo
    

    In Xojo you'd do this with the Extends keyword in the global method definition:

    Public Sub Foo(Extends FrameworkClass As c)
    
  • Erebos Manannán
    Erebos ManannánDec 2, 2018

    Having worked mostly with Elm, Python and Go lately, there's a LOT of things I miss when working with other languages.

    1. Good and clear ecosystems - Elm's automatic versioning is ingenious, Python has a pretty solid selection of libraries and with Pipenv it's even a bit better than standard, and Go is getting pretty solid as well. There's also no excessive use of microlibraries that add one basic function that everyone depends on multiple times in their dependency tree.

    2. Joy of development - constantly cursing why the language and it's standard libraries are missing very basic things, debugging being a pain, the runtime environments or compilers not catching super obvious errors (ever done a setTimeout(callback(), 1000) and realized that JS engines don't throw exceptions when setTimeout callback is undefined?), are just not things that happen a lot with these languages

    3. Clear standards - gofmt, PEP-8, and similar just save so much headaches. IDEs are configured by default to have one shortcut to reformat the whole file to the language standard, or even just reformat on save, etc. .. saves so much hassle overall

    4. Good concurrency model - Go's channels and goroutines are such an awesome thing to work with, and it always pisses me off when I have to work with any other style because they take so much more work to make reliable

    5. Clear frontend application model - with Elm you get so much certainty over how things are going to work due to how the language is designed. You avoid whole classes of problems because you just always build your applications in a specific way, and it's the right way for at least most cases.

    6. The flexibility - there's almost never a situation where I think "I wonder if I can do this in Python" and the answer is "no". With the whole language being built with well done duck typing instead of e.g. unpredictable type juggling, it's a breeze to do so many things. Similarly with Go, the interface system allows me to customize things a lot more than I would ever have expected from such a low level language.

    These are just the things that come to mind in the first few minutes.

Add comment