1. 18
  1. 7

    In the true spirit of J, here is a one-liner,

    '0123456789abcdef-'{~ (8 13 18 23 14 19}~(4#16),4,8>.11<.19&{) ?36#16
    

    Let’s read it as, the result of the string of hex digits and dash gets indexed by an integer array, whose 8 13 18 23 14 19 elements are respectively, 4 counts of the number 16 and a single number 4 and a single number, which is restricted in the range of 8 to 11 from the original 19th element of the integer array, which has random 36 elements each a non-negative integer less than 16.

    You can define the verb as

     uuid=:3 :'''0123456789abcdef-''{~ (8 13 18 23 14 19}~(4#16),4,8>.11<.19&{) ?36#16'
    
    1. 2

      Here’s my Dyalog one-liner : )

      ('----4',('89AB'[?4]),('0123456789ABCDEF'[?30⍴16]))[⍋⍋'xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx']
      
    2. 3

      I still find the APL version more readable than the J version. My understanding is that most of the clever ideas from J found their way back to at least Dyalog APL. I like J in principle because it is open source and free, unlike Dyalog, but in practice, the “magic numbers” of J are pretty offensive, and I really dislike that (as you can see in this definition) you close a definition with punctuation that isn’t used to open it. Not many languages do something so perverse.

      Maybe someday after I take a linear algebra class I will try again with APL.

      1. 3

        you close a definition with punctuation that isn’t used to open it. Not many languages do something so perverse.

        Check out Zimbu by Bram Moolenaar, of Vim fame. It does something similar.

        1. 2

          Thanks, I hate this. :)

        2. 2

          the “magic numbers” of J are pretty offensive

          J’s use of “magic numbers” is inherited from APL (Iverson invented both languages). Eg, in APL, sin(x), cos(x) and tan(x) are 1○x, 2○x and 3○x.

          In K (Kona) you would write _sin x, _cos x and _tan x. More readable than either APl or J in this case. Kona is an open source implementation of K.

          1. 3

            inherited from APL

            That doesn’t make them any less offensive, and j leans into them more heavily. A proposed solution to that particular case. And I say this despite preferring j to apl.

            1. 1

              The use of integers as names (aka magic numbers) is pervasive in APL and J.

              • In both languages, there are hundreds of operations that are denoted using integers. The trig functions are just the tip of the iceberg. It’s not feasible to invent glyphs for all of these operations, so the real alternative is to give them names, as K has done (and also BQN).
              • In both languages, the only compound data structure is the array. If the problem domain that you are modelling contains record-like data structures, then you use arrays instead of records, and instead of writing E.name, E.salary, E.department, you write E[0], E[1] and E[2]. If this isn’t satisfying, then the alternative is to add records to the language, as K has done.
              1. 1

                there are hundreds of operations that are denoted using integers. The trig functions are just the tip of the iceberg

                Hundreds? Are you talking about something other than ⌶/!:? Because I would argue those are more meta-lingual.

                instead of writing E.name, E.salary, E.department, you write E[0], E[1] and E[2]. If this isn’t satisfying, then the alternative is to add records to the language, as K has done

                This is a problem. I will add, dyalog has ‘objects’, and j has indirect locale references, both of which suffice to implement records. But these are non-compositional, because the language is array-oriented, not record-oriented. I would argue that the maximally compositional solution is something like:

                NAME=: 0
                SALARY=: 1
                DEPT=: 2
                Rx_AXIS=: _  NB. or _1, or whatever you like
                
                SALARY {"Rx_AXIS E
                

                But no one does this, of course. Associative arrays—like k, which you allude to, but multidimensional—would improve matters somewhat, but no one agrees with me about the right way to implement them :)

            2. 1

              True, but I think in general J leans into the idea a lot harder, since magic numbers are needed to create definitions and call libraries.

              Having trig functions inconveniently located isn’t a huge issue for me, since I need them on average once every five years.

            3. 2

              The problem with APL is that the indexing operation with brackets [] is a very odd notation, considering all other APL function behave as monad/dyad with strict right associativity. Every use of [] basically breaks the application order of the functions.

              J makes the indexing an actual function, which observe the normal right associativity. I would write it as the following to avoid excessive parentheses

              uuid =: 3 : 0
                h=.'0123456789abcdef'
                t=.'----4' 8 13 18 23 14}h{~?36#16
                t 19}~'8888888889abbbbb'{~h i.19{t
              )
              

              It’s probably better to keep it as a integer array and convert to character string in the end.

              1. 2

                Worth noting that this feature was added to APL first, before J was invented. But there is a certain lack of standardization of the name and semantics of this operator among the APL dialects.

                Dyalog has the “squad” operator (index ⌷ array). https://help.dyalog.com/latest/#Language/Primitive%20Functions/Index.htm

                1. 2

                  Yep. J is very similar in spirit to Iverson’s “Rationalized APL”, which introduced a lot of features that ended up in J.

                2. 1

                  Yep, beautiful.

              2. 2

                I also wrote a UUIDv4 generator in REXX, for fun: https://gist.github.com/deadpixi/8fb276a43f242bbce95177342d1b58d8

                This was all inspired by @eatonphil’s tinyprograms.org project.

                1. 2

                  This is much easier to read with comments than your tweet form. :) Nice post!

                  1. 2

                    Array programming noob here but most of this makes sense to me except the “variant nybble”. This sets three bits as described in section 4.1.1 in the RFC? Could you break down how it works?

                      ⍝ Force the variant nybble to be "RFC 4122".
                      t[19]←'8888888889abbbbb'[h⍳t[19]]⋄
                    
                    1. 2

                      Sure (and I kinda wrote that off the cuff, so check my math, please).

                      t[19] is the offset into the hex string that has the nybble whose top two bits need to be 10 (the third bit doesn’t matter in the RFC variant).

                      So, that nybble has four possible values: 1000 1001 1010 1011, in other words, 0x8 through 0xb.

                      So we take the index of the hex character (e.g. “f”) in the hex string h, which happens to convert it to its numeric value, and then use that as an index into the ‘8888888889abbbbb’.

                      That string is magic: the indices less than 0x8 are folded to 0x8, and the indices higher than 0xb are folded to 0xb, thus clamping the value in that range.

                      Basically the same as saying min(0xb, max(t[19], 0x8)).

                      Lemme know if that makes sense.

                      1. 3

                        Thanks very much for the explanation. As I understand it though the goal is just to set two bits to 0b10. Is there really no easier way to do that? Here’s what I’m thinking of in some C-like pseudo code:

                        t[19] = t[19] & 0b11110011;
                        t[19] = t[19] | 0b00001000;
                        

                        Does that just not translate well into array languages or am I missing something else?

                        1. 2

                          Nope, you’re not missing anything. Bitwise operations are definitely possible, but doing it that way just seemed more “natural”; to do a bitwise operation would’ve involved converting the character to a number and then the number to a bit vector and then back again (standard APL doesn’t have bitwise ops on arbitrary numbers, only bit vectors). This just did a bit of magic. :)

                          (Many APL implementations do have ways of doing bitwise ops directly on integers, it’s just not standardized. And I’m pretty sure Dyalog is smart about representing bit vectors as packed data in memory, for the record.)

                          1. 1

                            In j, a full complement of bitwise operators is available, specified using a truth table; bitwise and is 2b10001 b. and or is 2b10111 b. (leading 1 means bitwise rather than logical).

                            1. 1

                              It’s not a one-liner but this program could also be written as bitwise operations in Dyalog:

                              version←(4⍴2)∘⊤			⍝ version 4 ←→ 0 1 0 0
                              variant←4↑(⍉⍴⍤0∘1)		⍝ variant 1 ←→ 1 0 0 0
                              mask←⊖4↑((⍉⍴⍤0∘1)3-⊢)		⍝ mask 1 ←→ 0 0 1 1
                              
                              ⍝ returns the string representation of a 4×32 UUID bitmatrix
                              format←{('----',(⎕UCS ⎕UCS '0123456789ABCDEF'[1+2⊥⍵]))[⍋⍋'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx']}
                              
                              MakeRandomUUID←{ u←4 32⍴1=?128⍴2 ⋄ u[;13]←version 4 ⋄ u[;17]∧←mask ⍵ ⋄ u[;17]∨←variant ⍵ ⋄ format u }
                              
                            2. 2

                              8888888889abbbbb

                              isn’t this slightly biasing the randomness in those lower 2 bits? maybe I’m wrong but it seems like each of those bits will come out to 1 only about 37.5% of the time.

                              1. 1

                                …dang it I think you’re right. But it’s such a pretty little hack!

                                1. 2

                                  You could’ve used 88889999aaaabbbb to get to right distribution

                              2. 2

                                Sure (and I kinda wrote that off the cuff, so check my math, please).

                                You can validate the uuids easily with python if you want. The crux of the tinyprograms test script is:

                                from uuid import UUID
                                got = UUID(out, version=4).hex
                                if got != out.replace('-', ''):
                                  raise Exception