HNHacker News
TopNewBestAskShowJobs

icen

336 karma · joined October 26, 2015

submissionscomments
icen··on Roger Hui has died
The original description is misleading, yes.

The distinguishing between arities is a consistent feature of all of these languages. I find fluency comes quickly to it.

Edit: as for intuition between the two, this is sometimes not obvious, but I think it's a sensible generalisation from the monadic case to the dyadic. How else would you want to extend a fold to two arguments?

icen··on Roger Hui has died
It's no more magical than anything else. It just turns out that j has an operator that does exactly what you want.

Having such operators and functions is far from an uncommon experience in j.

icen··on Roger Hui has died
Dyadic `x / y` is different to monadic `/ y`
icen··on Roger Hui has died
/ does different things depending on the arity of the preceding function.

'f/ x' is 'insert' - i.e. reduce, so that '+/ 1+i.10' yields 55.

'x f/ y' is 'table', inserting 'f' between each pair to form a generalised multiplication table.

The documentation for j is unsurprisingly terse, but it is complete and very helpful.

https://code.jsoftware.com/wiki/Vocabulary/slash#dyadic

icen··on Wild claims about K performance
I agree; I don't think BQN values terseness more than k. I think k values terseness a lot.

I think it may be more convenient to write terse code in BQN over k, because there is greater facility to control function calls in a terse way. Things like under and over are quite common patterns that BQN finds easier (and shorter) than k.

I've not spent a great deal of time yet with BQN, so this could be wrong.

icen··on Wild claims about K performance
Sadly it also has to consider the time it takes to compile code like that - we grumble, but not too loudly, at the time LLVM takes to compile things. If the JVM took that same time whilst running live that wouldn't be acceptable.
icen··on Wild claims about K performance
You might enjoy the language created by the author of the article, then. BQN is a language that favours terseness, even above and beyond k. It is in many ways an improvement on APL and J, importantly through a more convenient set of operators than found in either.
icen··on From Competitive Programming to APL
You might be interested in Aaron Hsu's work on co-dfns, which is a compiler that compiles a subset of Dyalog APL to the GPU.

https://github.com/Co-dfns/Co-dfns

icen··on From Competitive Programming to APL
There are other things aside from array broadcasting that languages stand to learn from APL. I think that the biggest one is looking carefully at the clever choice of primitives.

To my knowledge other languages like Julia and numpy don't offer the same range of index-based primitives, like grades, or classifications, which are used pervasively in APLs.

APLs better support a pattern of computing some permutation of a set of arrays, and then viewing other arrays through that permutation, than other languages. You can do it, of course, in numpy, but it's not nearly as pleasant.

icen··on Double bongcloud: why grandmasters are playing the worst move in chess
As is so often the case in chess, a basic surveil of the theory can open up novelties in GM games.

The seminal text 'Winning with the Bongcloud', is heartily recommended for anyone looking to understand this new opening.

Here is a pdf link: http://i.4pcdn.org/tg/1401479151063.pdf

icen··on Double bongcloud: why grandmasters are playing the worst move in chess
Often in tournament chess you cannot offer a draw for the first 10 moves. However a 3-fold repetition is automatically a draw.

The idea is to try and provoke games happening, even if the players come to the table wanting a draw (someone can still botch their opening!). Ultimately though, if GMs want a draw, they'll get it.

icen··on J Notation as a Tool of Thought
Emacs has an excellent APL keyboard implementation (via gnu-apl-mode) which uses . as a prefix for the apl keys.
icen··on New JavaScript code generator in Idris 2
There seems to be a bit of confusion here. Idris doesn't provide the same tools and guarantees as Rust does to manage memory, and instead requires that implementations provide a garbage collector.

If you're producing code for a collected language, you can forward on all of the allocations to that language, and if not, you will need to implement one.

Zero-cost abstractions or not, Idris needs a GC from somewhere.

icen··on Ask HN: When will we have mobile browser extensions and ad-blocking?
I have uMatrix and uBlock Origin on Firefox for Android. This has been available for a long time.
icen··on Guix Further Reduces Bootstrap Seed to 25%
Not the name of the project (which is pronounced 'geeks') but the implementation of the shell and coreutils, as from the text in the article. This implementation is called 'Gash'.
icen··on J for C Programmers
q is k with all of the monadic verbs being replaced by English words: `+` is `flip`, `=` is `group`, `-` is `neg`, and so on. It also has q-sql, a SQL like sublanguage for conveniently working with tables. k has the verbs that implement it, but they're not really as convenient.
icen··on Why is Idris 2 so much faster than Idris 1?
As the workhorse example, consider the length-indexed Vector:

    data Vec : Nat -> Type -> Type
Each Vec has its length in its type. You get a type error if you try the following:

    f : Vec 3 Int -> Int
    f [1, 2, 3, 4] -- error: wanted a Vec 3 Int, found a Vec 4 Int
and with that, consider the following program:

    import Data.Vect
    
    readInts : (n : Nat) -> IO (Vect n Int)
    readInts 0 = pure []
    readInts (S n) = do
      x <- getLine
      rest <- readInts n
      pure $ (cast x) :: rest
    
    main : IO ()
    main = do
      length <- getLine
      ints <- readInts (fromInteger . cast $ length)
      pure ()
The above program doesn't know what the type of `ints` is in the do-block precisely. It has a length specified by the user! But we can reason about its length - and the type system would ensure that its length is propagated and handled properly everywhere we try to use it.
icen··on Why is Idris 2 so much faster than Idris 1?
Indeed - the Type/Kind hierarchy is unified when you write a dependently typed system. In simply typed lambda calculus, you have a system of values, types, and kinds, which are required to make sense of the structure. In dependent types, you have values, and types, which are also values.
icen··on Why is Idris 2 so much faster than Idris 1?
It is more that you can have values like `payload : if byte[TYPE_OFFSET] == TYPE_INT then Int else String`.

You are required to disambiguate the type before anything useful can be done. What has happened is that you have types being passed around by functions and evaluations which reflect dynamic behaviour at runtime - but there is a type statically known at all times for each part of the program.

icen··on Why is Idris 2 so much faster than Idris 1?
Dependent types let you do this. The type system ensures that you have all the types known at every point, and everything is sewn up before runtime - even though you don't know which type you will actually get ahead of time.

Types are values, and types can depend on values - so if your protocol tells you you should be getting a string, a string shows up in the type signature.

icen··on In Further Praise of Dependent Types
Yes, the actual code will always tell you exactly what it does.

There is always a necessary distinction between specification and implementation: if there weren't, one of them would have no value. We often suggest that the intent of a piece of code should be specified in comments, and this is in that category. This way, the compiler can read your comment and figure out if it needs updating! Languages without dependent types work this way too - but you have to try very hard indeed to be eloquent in type signatures, and being able to talk about values lets you say a bit more.

Coming onto your second point: if the function is only valid for some set of values of the given type, why is it 'not really called for' to create specialised types for it? If you call it with something else and it runs, it will be a bug. Newtypes or wrappers are common patterns.

I think that most mainstream typed languages have common constructs to offer that 'A or a B' value you want: this is solved via inheritance, `Either`, `Result`, or `std::variant`.

icen··on In Further Praise of Dependent Types
You can specify the 2-vectors on the unit circle as "{ (x, y) : x, y in R | x^2 + y^2 - 1 < 0.000001 }" instead.
icen··on In Further Praise of Dependent Types
If you're passingly familiar with Haskell or similar languages, you could give Idris a try.

https://www.idris-lang.org/

icen··on In Further Praise of Dependent Types
(Not OP.)

When moving from dynamically/untyped/unityped languages to those with types, you are liberated from having to check the shape of inputs. If you're given something that claims to be a list of strings, it absolutely is a list of strings, and you can proceed without thinking about it's list-ness or string-ness any further.

When moving from a typed language to a dependently typed language, you are liberated from having to check the state of inputs. You don't have to worry that your file handle is closed, or ensure that you update the state of the object correctly to satisfy some protocol (if you don't, you will be picked up on it!). If you are writing a parser, you don't need to check that the parser returned the form that you just called - you know upfront.

There are other niceties: the type of the function is always a very rough exposition of what the function should do. With dependent types, it becomes considerably clearer. Consider these two functions, which could have very similar implementations (in some Idris-like syntax):

    f : Int -> Int -> Bool
    data T : Int -> Int -> Type where
         Tz : T 0 x
         Ts : T a b -> T (a + 1) (b + 1)
    g : (x : Int) -> (y : Int) -> Maybe (T x y)
Given just the structure, it should be a bit clearer as to what's going the functions intend to do.
icen··on In Further Praise of Dependent Types
This is standard notation.

The `x : A` supposes a value, `x`, inhabiting a type `A` (like in Rust, or ML, or maybe `A x` in C).

On the right hand side of the turnstile, we have `B(x) : Type`, which is roughly what it looks like: `B` is a type parameterised by `x` (note - it is not parameterised by `A`, but by `x`! It is allowed to know about the value `x`).

The central turnstile is a contextual relation. The left hand side is an ambient known value, and the right hand side is something that be defined in that context. You can read it informally as "given", so this phrase is "given x: A, we can construct a type B(x)".

icen··on Is APL Dead? Not anymore
You build vectors of the other conditions, and subtract them before the running sum.
icen··on Most(ly dead) Influential Programming Languages
It wouldn't surprise me if all of these languages were alive in some way or other. There are MLs and APLs and Pascals alive and well.
icen··on Most(ly dead) Influential Programming Languages
It keeps going!

      3 3 3 ⍴ ⍳10

     1  2 3
     4  5 6
     7  8 9
       
    10  1 2
     3  4 5
     6  7 8

     9 10 1
     2  3 4
     5  6 7

      M ← 3 3 3 ⍴ ⍳10

      M*M

    1.00000E0  4.00000E0  2.70000E1
    2.56000E2  3.12500E3  4.66560E4
    8.23543E5  1.67772E7  3.87420E8

    1.00000E10 1.00000E0  4.00000E0
    2.70000E1  2.56000E2  3.12500E3
    4.66560E4  8.23543E5  1.67772E7

    3.87420E8  1.00000E10 1.00000E0
    4.00000E0  2.70000E1  2.56000E2
    3.12500E3  4.66560E4  8.23543E5
That's 3 nested 3x3 matrices, and we take the pointwise product of each of them with just the usual multiplication operator.

How many maps did you want?

icen··on Stages of denial in encountering K
< grades upwards: for each element, the element of the returned array is its position if the array is sorted.

    x: 10?A:"abcdefghijklmnopqrstuvwxyz"
     x
    "ysyselacwl"
     +`x`y`z!(x;<x;<<x)
    x   y z
    --- - -
    "y" 6 8
    "s" 7 5
    "y" 4 9
    "s" 5 6
    "e" 9 2
    "l" 1 3
    "a" 3 0
    "c" 8 1
    "w" 0 7
    "l" 2 4
So <x gives a sort index, <<x tells you how far each element is from the minimum in that sort index
icen··on Stages of denial in encountering K
dyadic | is or, or max. So e.g. 4 = |/1 2 3 4
← PreviousPage 2 of 5Next →