[HN Gopher] Dependently Typing R Vectors, Arrays, and Matrices
       ___________________________________________________________________
        
       Dependently Typing R Vectors, Arrays, and Matrices
        
       Author : matt_d
       Score  : 82 points
       Date   : 2023-04-11 05:07 UTC (17 hours ago)
        
 (HTM) web link (arxiv.org)
 (TXT) w3m dump (arxiv.org)
        
       | programLyrique wrote:
       | Another recent (2020) work on types for R: "Designing types for
       | R, empirically" https://dl.acm.org/doi/abs/10.1145/3428249
        
       | hackandthink wrote:
       | "2. A Primer on R Vectors, Arrays, and Matrices"
       | 
       | This should be referenced in every R Tutorial.
        
       | blackbear_ wrote:
       | Types in R make absolutely no sense to me, and the whole
       | apply/lapply/sapply/tapply/vapply madness gives a feeling of
       | hacks taped together just enough so that the thing does not fall
       | apart.
       | 
       | > What a programmer might think of as a primitive value is often
       | actually a vector containing precisely one element.
       | 
       | Say what?!
        
         | sixbrx wrote:
         | R's pretty serious about it, it's why for example length("abc")
         | returns 1, which is a head-scratcher for people coming from
         | Python.
        
           | bachmeier wrote:
           | Well they could read the documentation. nchar("abc") returns
           | 3.
           | 
           | That's a lot easier for new programmers than len("abc"),
           | which requires them to understand Python's string
           | implementation. len("abc\n") returns 4 - confusing as hell
           | for a new programmer working with a very common data analysis
           | problem, because "length" is too generic to know it's the
           | number of characters. nchar("abc\n") being 4 makes sense,
           | because "\n" is a character representing a line feed.
           | Applying the same logic, len(137) returns TypeError: object
           | of type 'int' has no len(), when it should be 3.
           | 
           | At the end of the day, the programmer will always have to
           | make an effort to learn the language they're using.
        
         | dan-robertson wrote:
         | This is a bit like other vector/array languages like Matlab or
         | APL: 145 isn't considered a 'scalar' but rather an array
         | containing one element. Though it can make defining how to deal
         | with different shapes for some operation like + tricky. One
         | idea is cycling when one vector/axis is shorter. Another is to
         | special case vectors with one element. Another is for these
         | singletons to be, in some sense, zero dimensional arrays and
         | then you can just have broadcasting with errors if one shape
         | isn't a prefix of the other.
        
         | PheonixPharts wrote:
         | > Say what?!
         | 
         | Maybe it's because R is heavily used by non-programmers, there
         | is this weird belief that R should be easy to grok by anyone
         | with programming experience, and if they don't immediately get
         | it, it must be R's fault. Of the programming languages I've
         | used, R is much closer in terms of changing your view of
         | programming to Haskell and Lisp (a good chunk of R essentially
         | is a Lisp) than most other 'blub' languages [0]
         | 
         | R certainly has quirks, but most of them come from being one of
         | the oldest programming languages out there (considering it is
         | ultimately an open implementation of S which came out in 1976)
         | and so has a lot of programming patterns that may not be
         | familiar for modern programmers.
         | 
         | Not understanding that R is fundamentally a vector based
         | language and what a vector based language is is akin to
         | approaching Haskell and not having any understanding of
         | Functional Programming, finding no for-loops and saying it's a
         | silly language.
         | 
         | If you are someone who is very curious about programming
         | languages and different programming paradigms, and likes doing
         | numeric/statistical computing, I highly recommend approaching R
         | with a very open mind, and being willing to explore it more
         | carefully. As just one example: one of it's multiple object
         | systems is using Generic functions in the same way that Common
         | Lisp's CLOS does. The best example is R's `plot` function which
         | is a generic function dispatching based on the class of the
         | argument. Again this is something that less experienced
         | programmers see as confusing and stupid, whereas someone with a
         | better understanding of the development of OOP will likely find
         | this very interesting.
         | 
         | 0. http://www.paulgraham.com/avg.html
        
           | andersentobias wrote:
           | Would you be interested in tutoring a experienced programmer
           | (me) in using R?
        
         | huijzer wrote:
         | Yes. No function overloading + no object oriented = verbose
         | function names.
         | 
         | What I find even more crazy is that some of the R functions
         | take function names as strings as argument. For example, taking
         | "sum" as an argument will then apply the sum function to the
         | dataframe.
        
           | wodenokoto wrote:
           | In fairness, much newer frameworks such as pandas do the same
           | ie `df.agg("sum")`.
           | 
           | What is it about function names that overloading would solve
           | that singular dispatch can't already do?
        
             | 0cf8612b2e1e wrote:
             | To carry water for pandas, you can also pass functions into
             | that pattern: 'df.agg(np.sum)'
        
               | kgwgk wrote:
               | Just like in the R example, I think.
        
           | vharuck wrote:
           | >No function overloading + no object oriented = verbose
           | function names.
           | 
           | Forgive me if I misunderstand, but are you saying R doesn't
           | have function overloading? Because R's S3 class type system
           | relies entirely on function overloading to make up for not
           | having class-bound methods. Look at the result of
           | `methods("print")` to see how overloaded that function is.
           | 
           | The real problem with this system is that _every function_ is
           | in the general namespace. It gets hard to avoid name clashes.
           | Some packages have taken to appending prefixes to their
           | function names (e.g., every function from the stringi package
           | starts with  "stri_").
           | 
           | >What I find even more crazy is that some of the R functions
           | take function names as strings as argument. For example,
           | taking "sum" as an argument will then apply the sum function
           | to the dataframe.
           | 
           | Think of it like a "naturalized" macro language. When you
           | know you'll always use `sum`, you can pass the function
           | object itself. But character vectors are easier to deal with
           | in logic and control flows. For a toy example, let's say you
           | have a dataset with some columns that contain addresses, some
           | that contain dates, and so on. You could write an overall
           | cleaning function that takes the dataset and a "format" table
           | to be a lookup between column names and functions to use1. R
           | even has `getFunction` to make this simple.
           | 
           | 1Because functions are regular objects in R, the format table
           | could have a list column with the functions. But I've found
           | my code looks more cluttered when using arbitrary functions
           | in logic. And function names are easier to handle when saving
           | data frames to files or databases.
        
         | rscho wrote:
         | A 'just-screwed-enough-to-still-be-usable' version of Scheme is
         | apparently a reasonable recipe for success in the PL design
         | world: JavaScript and R have a genesis in common. I have to say
         | that I long for an array language that's closer to original
         | Scheme (for the boilerplate parts of statistical programming)
         | or/and APL (for purely statistical stuff).
         | 
         | Regarding the typing story, I think a contract system (R has
         | libs for that) rather than static types would be far better.
         | Nice as they are, dependent types are not easy to use, and
         | pretty much a nightmare for exploratory statistical work. Since
         | most of the runtime is spent iterating on vectors anyways,
         | contracts would IMO have a much better usability story.
        
         | nequo wrote:
         | Everything being a vector is one of the best things about R.
         | You can write a function that looks sensible for scalars:
         | in_circle <- function(x, y) {         x^2 + y^2 <= 1       }
         | 
         | It works as you would expect:                 > in_circle(.2,
         | .3)       [1] TRUE       > in_circle(1, 1)       [1] FALSE
         | 
         | But you can also evaluate `in_circle` for multiple points with
         | a single function call. You can pass in vectors as arguments,
         | and you'll get back the results as a vector:                 >
         | in_circle(c(.2, 1), c(.3, 1))       [1]  TRUE FALSE
         | 
         | Since the columns of a data frame, a tibble, or a data table
         | are all vectors, it becomes very natural to apply simple
         | functions to them. With tibbles:                 > data <-
         | tibble(a = c(.2, 1), b = c(.3, 1))       > data       # A
         | tibble: 2 x 2             a     b         <dbl> <dbl>       1
         | 0.2   0.3       2   1     1         > data %>% mutate(inside =
         | in_circle(a, b))       # A tibble: 2 x 3             a     b
         | inside         <dbl> <dbl> <lgl>        1   0.2   0.3 TRUE
         | 2   1     1   FALSE
        
         | kuhewa wrote:
         | > /lapply/sapply/tapply/vapply madness gives a feeling of hacks
         | taped together just enough so that the thing does not fall
         | apart
         | 
         | Hard to argue against that when the argument for whether to
         | pass to simplify2Array or leave the result as a list is:
         | 
         | sapply(..., simplify = )
         | 
         | but is
         | 
         | mapply(..., SIMPLIFY = )
         | 
         | It kinda reminds me of English pronunciation as far as
         | consistency of conventions goes
        
         | mamcx wrote:
         | > Say what?!
         | 
         | This is the sensible design!
         | 
         | Is like "In a OO language, 1 is a object"
         | 
         | But here, this is an _array language_ , so it makes ton of
         | sense that 1 is an array.
         | 
         | And in a relational language, 1 should be a relation.
        
       ___________________________________________________________________
       (page generated 2023-04-11 23:02 UTC)