Fast Arcsine in Apple
One can improve on arcsine's performance with Horner's method and then bittwiddling.
One can improve on arcsine's performance with Horner's method and then bittwiddling.
Allison (1989) gives an example of tying the knot to build an immutable, doubly-linked list from a list. However, this does not support the usual insertion and deletion.
Robert Harper points out that dynamically typed languages are a subset of statically typed languages. To wit, we could handle J values in Haskell with something like:
J's #: (base) can be used to compute the offset of an array element as it would appear in memory. Why so? x #: y is +/w*y , where w is formed as */\.}.x,1 — w is precisely the strides of an array with dimensions x .
Variable types (à la System F) are not templates, though one can think of them as type schemata. A template
After paraphrasing a computation I had found in a paper , I realized I had ruined its performance.
Apple outperforms R in important cases.
Writing an assembler turns out to be an interesting example: one needs to calculate distances between jumps and their target labels, and the target may appear after the label. It turns out that one can write a one-pass assembler using the tardis monad or in Curry , using logic programming.
For an array with dimensions \(n_1,n_2,\ldots n_r\) stored in column-major order, an element with indices \(a_1,a_2,\ldots a_n\) is located at offset
One motivation for Apple was demonstrating typed array programming. Shape types are rich; we can use types as witnesses as in QuickCheck , generating test cases that are shape-correct.
Consider a softmax layer from Aditya Srinivas Menon's tutorial :
Apple , being a JIT compiler with shape types, is able to do a number of optimizations based on inferred dimension (and rank). Rank is almost always known in practice, so such optimizations are pertinent.
I just finished adding another mid-end to my Apple JIT compiler, motivated to get rank facilities right . However, there remain significant shortfalls.
Compilers and linkers put their own version information in ELF binaries; we can inspect with readelf on the .comment section, to wit:
One PL nihilism is "all languages are the same." This is not so—general-purpose languages have converged on procedures, but languages that differ nontrivially are used in computing.
Universal ctags points the cursor to line numbers rather than the actual definition (as language servers do). Vim uses ex ( :h Ex-mode ) commands as destinations . We want to generate call cursor(line,col) for each identifier.
An established problem in functional programming is the question of evaluation order (see Hudak , §2.2). Haskell offers seq ; which allows the programmer to magically introduce dependencies in evaluation order and thence subvert lazy evaluation. Sometimes this is necessary; see the foldl foldl' example .
nm shows symbols defined in an executable. GHC includes library name in symbols, so we can use this to inspect libraries that make it into the compiled binary. GHC uses z-encoding so we pipe the output of nm into an ad-hoc invocation of sed , viz.
GHC embeds linker flags into the final ELF binary:
I turned to my own Jacinda to view the output of otool and was pleasantly surprised.
The GNU sed manual offers the following to join backslash-continued lines:
One can define a Num instance in Haskell for ASTs of expressions, viz.
If we wish to preserve global uniqueness of names during \(\beta\)-reduction, we have to \(\alpha\)-rename before each substitution. Consider:
Haskell puts all side effects in the IO monad, which passes around the RealWorld . This is unsatisfactory for a number of reasons, and Haskellers have spilled much ink on effects systems . As I recently noted , there are distinctions in how one handles effects at the logical level: in particular, randomness is different from array writes.
C has a reputation for being a "hacker" language, in contrast to say Haskell, which is abstract with ties to logic and category theory. There is even the quip "C is a portable assembler".
Logic programming fails for many reasons; interestingly it fails to integrate with imperative programming or export its constructs. Haskell's monadic I/O—explicitly passing a RealWorld —offers a nice demonstration.
Egison advocates a pattern-match oriented style of programming and offers poker hands as an example:
My Apple compiler started as an experiment in what I would implement as compiler for an array system. Several parts did not pan out so I offer my warnings and advice to other array language implementers.
Functions are compiled to machine by a convention of jumps and registers; one calls a function by jumping to its location in memory. These jumps are relative and particular functions (say, malloc ) may be loaded at different memory locations, so the machine code for a function cannot be pinned down and in fact is contingent on every function that it calls.
Building a respectable compiler requires basic blocks in order for liveness analysis to be performant. Consider my own Apple compiler :
I previously wrote about typing elliptic fourier series ; my Apple compiler is now capable of computing the offsets and also the relevant coefficients.
To compute a 7-day moving average:
As I wrote about previously I am working on a typed array language with the case of elliptic Fourier series as an example.
Compilers are written as a pipeline: in particular, instruction selection and register allocation are different phases. GHC , for instance, uses maximal munch for instruction selection and a variety of register allocators. However, on x86-64 (for instance), register allocation constrains the particular instruction encodings, which affects the cost of some instructions.
Writing a practical JIT is somewhat complicated and in fact depends on the assembler; here I present a full example in Haskell. Notably this JIT/assembler is capable of calling procedures in system libraries (i.e. malloc , free )
"Typing Haskell in Haskell" makes the implementation of type systems concrete for programmers; recent developments in type theory have much to offer but are not developed to this depth even in theory. Row types are particularly juicy because one does not lose type inference ; we can use the exact same unification approach and need not resort to ordered contexts or focalization.
Elliptic Fourier series are a good example to kick the tires on array programming systems; J and Python , however, are both dynamically typed.
APL is truly different from other languages; nearly every language uses lexical scoping to express composition. Both GHC Haskell and GCC/Clang use a stack for variables across procedures because it models how variables become available (FIFO). Putatively different languages are constrained by the same fundamentals.
Jacinda can imitate cloc by combining it with other command-line tools on the Unix command-line, viz.
One common oversight in Haskell compilers is failing to intern identifiers using Int s and failing to prefer IntMap s and IntSet s. The PureScript compiler , for instance, uses Map s as of writing.
Suppose we want to clean up all TODO s in a branch before merging. We can check for TODO s introduced with
One would like to be able to find all language extensions in a given Haskell project in order to populate the other-extensions field of the .cabal file ( cabal-install uses this for dependency resolution).
J and APL support (and encourage) a certain form of programming without error handling or library code reuse. The alternative wisdom goes against typical programming but it works together.
Suppose one would like to process compiler output to include span ; vim uses awk ( mve.awk ) to do this.
I am in the process of adding a deduplication builtin to my [Jacinda](https: //hackage.haskell.org/package/jacinda) language. It's something I've wanted often enough and evidently part of the reason people use awk (it is the way to deduplicate on the command line without changing order).
One can use Jacinda to generate tags files for vim. This amounts to string processing with regular expressions, which we can do in a functional style, skirting Universal Ctags .
Suppose we wish to inspect our PATH . Then we can make it more readable using awk :
GHC includes span information in compiler errors, but not in a format suitable for vim:
The !-modality is a comonad . In particular, it is a functor; we can lift any function/procedure to work on perennial types with map , \( A \multimap B \vdash !A \multimap !B \).
Concatenative languages lend themselves to rewriting because they do not bind variables and thus do not incur any confusion with renaming/scope (compositional rather than applicative).