Comment by kelas

4 months ago

> i assume that in most array languages, you also create "words" or however you want to call functions, to reuse code.

sure, that's a very useful feature, like elsewhere.

> I wonder about a purely aesthetic issue: how does it look to interleave those symbols with user-defined words that by nature will be much, much longer, i.e. "create-log-entry" or "calculate-estimated-revenue".

strictly speaking, dashes and underscores in k can't even be a part of identifier - they are core language primitives. it is very uncommon to see java-like identifiers like CalculateEstimatedRevenue, why would you want that?

to your question:

here's a bit of an oddity: all user-defined functions and core language operators can be called using functional notation:

  v:1 2 3        / some vector
    v+v          / usual infix notation, two operands: left and right
  2 4 6
   +[v;v]        / same as infix, but called as it were a function.
   2 4 6

  add:{x+y}      / a user-defined function: a lambda with a name and two operands.
  add[v;v]
   2 4 6

but there is an important distinction between the two. you can't use your `add` function infix, you must call it as a function, and there are good reasons for that:

  2 add 2         / that's not gonna work

that said, mixing language primitives with function calls looks and reads just fine:

  +/add[v;v]
 12

hope this helps!

How does that scale up to program that's thousands of lines? What if you have a hundred different vectors? You're not going to be calling them v1, v2, ...

So does it end up as

    v_sepallength: 11 14 12
    v_sepallthickness: 1.3 1.5 1.2
    mul[v_sepallength;v_sepalthickness]

Or, do you just not do that sort of stuff in these languages? I'm not very familiar with them, but I have ended up with some pretty long programs using Pandas in Python.

  • > Or, do you just not do that sort of stuff in these languages?

    i tell you more. it is very much recommended to avoid doing this sort of stuff in all languages.

      v_sepallength: 11 14 12
      v_sepallthickness: 1.3 1.5 1.2
      mul[v_sepallength;v_sepalthickness]
    

    no:

       /sepal:lengths and stroke widths
       spl:[l:11 14 12;w:1.3 1.5 1.2]    /this is your "struct", if you will
      
       spl.w
      1.3 1.5 1.2    /proof
    
       */spl
      14.3 21. 14.4  /for mul, we don't even have to bother with field names
    
       */spl`l`w     /but if you insist, lets make it explicit
      14.3 21. 14.4
    
    

    to produce a "factory" for well-formed spl objects is a no-brainer as well.

    why we don't use v_ prefix:

      1. everything what can be a vector should be a vector.
      2. we can't use underscore anyway - it is an operator.

  • very important things should have short names. locals you're immediately operating upon should have short names. short names should be used in a consistent way.

    less important things can have longer names. variables in a broader scope can have longer names.

    if you have a hundred different vectors, don't just dump them in a pile; put them in dictionaries, tables, namespaces, or scopes.

    • exactly.

      the complexity is built differently in k.

        * namespaces do exist, and are just as useful as they are in c++ and especially my beloved *sun.misc.unsafe*. i recommend.
      
        * instead of passing 20 arguments to a function (which is impossible - the limit is lower), we pass a dictionary if we have to. k **pretends** that everything is passed by value, but in reality it is much smarter than that.
      
        * notion of *scopes* is a bit of a non-sequitur here, but it is fundamentally important that there is no *lexical scoping* in k. the only two scopes which are available from the scope of a lambda are exactly *local* and *global*. and for as long as your function doesn't mess around with global scope or i/o (which is essentially the same thing), it remains pure, which is super cool. this design is not just for simplicity - it is for a good reason, and more than one.
      
        * the above doesn't mean that it is impossible to create a *closure* in k and pass it around as a value.
      
        * functions take up to three implicit arguments - named x,y and z (they can be renamed explicitly, but why not just document their semantics instead, in-situ?). all you need to do to declare xyz is reference them in the function definition. in competent k code, you'll rarely see a function with more than xyz.
      
       * in k community, we don't use upper case unless the apartment is on fire. god forbid.
      
       * shorter names and more documentation, and there will be joy.