Giter Club home page Giter Club logo

lambda.r's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

lambda.r's Issues

Warning message when a lambda.r function with type constraints returns an object with type information.

This occurs when,

Base case with no warning,

load_data(user_id) %::% character : z
load_data(user_id) %as% { data.frame(x='a', y=1) }
load_data('u')
x y
1 a 1

Warning case where load_data returns a data.frame with,

class(Transaction(data.frame(x='a', y=1), 'raw'))
[1] "Transaction" "data.frame"

load_data(user_id) %::% character : z
load_data(user_id) %as% { Transaction(data.frame(x='a', y=1), 'raw') }
load_data('u')
x y
1 a 1
Warning message:
In if (class(result) %in% sapply(raw.args, class)) { :
the condition has length > 1 and only the first element will be used

Preserve unevaluated arguments during dispatching

Not sure if this is possible, but it would be nice to preserve lazy evaluation under certain circumstances. Calling list(...) evaluates all arguments passed to the function. Possibly a clever use of substitute could work, otherwise it will likely require low-level C changes.

BUG: install.packages("lambda.r") fails in R-devel w/ _R_CHECK_LENGTH_1_LOGIC2_=true

In R-devel, there are now optional checks asserting that x && y is not called with length(x) > 1 or length(y) > 1. These checks can be enabled with _R_CHECK_LENGTH_1_LOGIC2_=true - I'll assume they'll eventually will be on by default. I guess that CRAN will enable them before that. I know that the CRAN incomin checks already have it enabled.

lambda.r produces:

Error in !is.na(line <- it()) && line$token != "SPECIAL" : 
  'length(x) = 9 > 1' in coercion to 'logical(1)'

already when installed.

Example

$ Rscript --version
R scripting front-end version 3.6.0 Under development (unstable) (2019-03-09 r76216)

$ _R_CHECK_LENGTH_1_LOGIC2_=true R --vanilla --quiet
> install.packages("lambda.r")
Installing package into/home/hb/R/x86_64-pc-linux-gnu-library/3.6’
(aslibis unspecified)
--- Please select a CRAN mirror for use in this session ---
trying URL 'https://cloud.r-project.org/src/contrib/lambda.r_1.2.3.tar.gz'
Content type 'application/x-gzip' length 25559 bytes (24 KB)
==================================================
downloaded 24 KB

* installing *source* packagelambda.r...
** packagelambda.rsuccessfully unpacked and MD5 sums checked
** R
** byte-compile and prepare package for lazy loading
Error in !is.na(line <- it()) && line$token != "SPECIAL" : 
  'length(x) = 9 > 1' in coercion to 'logical(1)'
Error: unable to load R code in packagelambda.rExecution halted
ERROR: lazy loading failed for packagelambda.r* removing/home/hb/R/x86_64-pc-linux-gnu-library/3.6/lambda.r* restoring previous/home/hb/R/x86_64-pc-linux-gnu-library/3.6/lambda.rThe downloaded source packages are in/tmp/RtmpFG73rb/downloaded_packagesWarning message:
In install.packages("lambda.r") :
  installation of packagelambda.rhad non-zero exit status

Session info

> sessionInfo()
R Under development (unstable) (2019-03-09 r76216)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Ubuntu 18.04.2 LTS

Matrix products: default
BLAS: /home/hb/software/R-devel/trunk/lib/R/lib/libRblas.so
LAPACK: /home/hb/software/R-devel/trunk/lib/R/lib/libRlapack.so

locale:
 [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C              
 [3] LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8    
 [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8   
 [7] LC_PAPER=en_US.UTF-8       LC_NAME=C                 
 [9] LC_ADDRESS=C               LC_TELEPHONE=C            
[11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

loaded via a namespace (and not attached):
[1] compiler_3.6.0 tools_3.6.0    tcltk_3.6.0   
> 

Nested %as% does not work

Here is an example:

require(lambda.r)
Function(x) %when% { is.function(x) } %as% x
addn.factory(n) %::% numeric : Function
addn.factory(n) %as% {
    addn(x) %as% {
        x + n
    }
    Function(addn)
}

Error in get_name(it) :
  Function must start with a symbol (instead of '(')

How do type check constructors?

For example, this would be legit, but nonsensical:

PointCartesian(x, y) %as% list(x = x, y = y)
PointCartesian("a", "b")

How do you make sure that both x and y are numerics?

No acceptance NULL for a default value

No way to supply NULL as a default value with typed functions

Portf_S(name) %::% character: list
Portf_S(name) %as% {Portf_S(name, NULL)}
Portf_S(name, endDate) %::% character: Date: list   
Portf_S(name, endDate) %as% {
    obj = mg(name)
    if(!is.null(endDate)) obj@endDate = endDate
    list(
            name = name, 
            invID = obj@invID,
            obj = obj,
            invType = getInvType(name),
            cacheKey = GenerateKey(name))}

Function parameter matching bug

Example as below, lambda.r function is not working the same as R functions:

for function call f1(Sys.Date(), 1,2) ,
Sys.Date() should be matched to start by default. But it is not.

library(lambda.r)
f1 (start = Sys.Date(), ...) %as% {
vs <- list(...)
print(vs)
NULL
}
f1(Sys.Date(), 1,2)
[[1]]
[1] "2016-07-27"
[[2]]
[1] 1
[[3]]
[1] 2
NULL
f1(start = Sys.Date(), 1,2)
[[1]]
[1] 1
[[2]]
[1] 2
NULL

f2 <- function(start = Sys.Date(), ...) {
vs <- list(...)
print(vs)
NULL
}
f2(Sys.Date()-1, 1, 2)
[[1]]
[1] 1
[[2]]
[1] 2
NULL
f2(start = Sys.Date()-1, 1, 2)
[[1]]
[1] 1
[[2]]
[1] 2
NULL

dplyr pipes + arithmetic functions causes parsing error

Using arithmetic operators in function form (e.g. *) causes an error when used in conjunction with dplyr pipes and lambda.r. See example below:

require(dplyr)
require(lambda.r)

## works
foo1(bar) %as% {
    bar * 2
}

## works
foo2(bar) %as% {
    `*`(bar, 2)
}

## works
foo3 <- function(bar) {
    bar %>% `*`(2)
}

## error - "Error in parse(text = text) ... unexpected '*'"
foo4(bar) %as% {
    bar %>% `*`(2)
}

EDIT: this seems to be specific to multiplication; addition + works

typeclass and multiple deriving

typeclass and deriving are very useful for composing interfaces.
and it should be easy mapping typeclass to S3 class in R.

Use of a return type variable throws an error.

I encountered a bug with this behavior when using a type variable for the return type for a lambda.r function definition:

 Point(x,y) %as% list(x=x,y=y)
distance(a,b) %::% Point : Point : z
distance(a,b) %as% { ((a$x - b$x)^2 + (a$y - b$y)^2)^.5 }

After defining the above type and function, executing this in R throws an error:

> a <- Point(1, 2)
> b <- Point(3, 4)
> distance(a, b)
Error in arg.types[[x]] : subscript out of bounds

Using an explicit type constraint works properly for the return type,

 Point(x,y) %as% list(x=x,y=y)
distance(a,b) %::% Point : Point : numeric
distance(a,b) %as% { ((a$x - b$x)^2 + (a$y - b$y)^2)^.5 }

Executing in R,

> a <- Point(1, 2)
> b <- Point(3, 4)
> distance(a, b)
[1] 2.828427

README notes

Just two thoughts on the README.md.

  1. I think it is worth explicitly mentioning that capitalizing a function name makes it a type constructor.
> Int(x) %as% x
> int(x) %as% x
> Int(1)
[1] 1
attr(,"class")
[1] "Int"     "numeric"
> int(1)
[1] 1
  1. There is a line in the Auto Replace and Sealing Definitions that is confusing:

For example take this simple reciprocal function. It has two three clauses and two type constraints. There is an explicit bug in the body of the second variant. Note that the signatures for variants 2 and 3 are identical and the only thing that distinguishes them are ther type constraints.

Note "two three clauses". Also it would be useful to define the term "clause". I believe you are referring to a block of function variants associated with a single type constraint, but that is only my best guess based on context. Also I would suggest specifically saying "the body of the second variant of the first clause". Lastly, ther -> their.

Sorry to be a stickler - I comment because I care! Great work on the package.

Type constraints that use numeric fail for integers

The reason Is that an integer does not inherit from numeric in S3.

> class(as.integer(4))
[1] "integer"
> is.integer(as.integer(4))
[1] TRUE
> is.numeric(as.integer(4))
[1] TRUE

Instead what is needed is to convert a numeric reference to is.numeric(), the same way that Function maps to is.function().

Inheritance of class breaks type safety

If we define the following:

TypeA(x) %as% x

TypeB(y) %as% y

multiplyA(a, b) %::% TypeA:TypeA:TypeB
multiplyA(a, b) %as% {
  TypeB(a * b)
}

Then we can create the following value:

r$> myB <- multiplyA(TypeA(2), TypeA(3))

r$> myB
[1] 6
attr(,"class")
[1] "TypeB"   "TypeA"   "numeric"

The following should throw an error, because myB should not be a TypeA.

r$> multiplyA(myB, myB)                                          
[1] 36
attr(,"class")
[1] "TypeB"   "TypeA"   "numeric"

S4 support in inputs

TestRefClass <- setRefClass("TestRefClass",
    fields = list(
        test_date = 'Date',
        test_num = 'numeric'
    ))

TestLambdaFun(test_obj_) %::% TestRefClass : TestRefClass
TestLambdaFun(test_obj_) %as% {
    return(test_obj_)
}


testObj <- TestRefClass$new(
    test_date = as.Date('2015-01-01'),
    test_num = 1)

Let's try to pass testObj to TestLambdaFun

> TestLambdaFun(testObj)
<S4 Type Object>
attr(,".xData")
<environment: 0x7ff36ae0b638>
attr(,"class")
[1] "TestLambdaFun" "TestRefClass" 
Warning message:
In class(result) <- c(type, class(result)) :
  Setting class(x) to multiple strings ("TestLambdaFun", "TestRefClass", ...); result will no longer be an S4 object

Equivalent of `let` in lisp?

Here is an example:

ColorRGBhex(hexcode) %::% character : .
ColorRGBhex(hexcode) %when% {
    length(hexcode) == 1
} %as% {
    ColorRGBhex_(tolower(hexcode))
}
ColorRGBhex_(hexcode) %::% character : .
ColorRGBhex_(hexcode) %when% {
    all(strsplit(hexcode, "")[[1]] >= "0")
    all(strsplit(hexcode, "")[[1]] <= "f")
} %as% {
    Color_(hexcode)
}

It would be nice if I can write it like this:

ColorRGBhex(hexcode) %::% character : .
ColorRGBhex(hexcode) %let% {
    hexcode = tolower(hexcode)
    hex_chars = strsplit(hexcode, "")[[1]]
} %when% {
    length(hexcode) == 1
    all(hex_chars >= "0")
    all(hex_chars <= "f")
} %as% {
    ColorRGBhex_(hexcode)
}

This saves both typing and computation time.

Function types

It would be awesome to be able to do something like this:

require(lambda.r)
Function(x) %when% { is.function(x) } %as% x
addn.factory(n) %::% numeric : Function(numeric : numeric)
addn.factory(n) %as% {
    addn = function(x) {
        x + n
    }
    Function(addn)
}

Seal functions by default

Only allow consecutive function clauses by default. Provide an option to allow a function to be 'generic' and be added to iteratively.

Depends on package 'parser'

I've just installed lambda.r, and when trying to load the package it fails with an error: "Error: package 'parser' could not be loaded.

A search on CRAN says that 'parser' has been removed.

More specific error messages in guards

For example:

OddNumber(x) %::% integer : .
OddNumber(x) %when% {
    (x %% 2) == 1
} %as% {
    x
}

Feed it an even number and it will complain:

> OddNumber(2L)
Error in UseFunction(type.fn, type.name, ...) :
  No valid function for 'OddNumber(2)'

But it doesn't say what exactly went wrong. How about something like this:

OddNumber(x) %::% integer : .
OddNumber(x) %when% {
    if(x %% 2 == 1) TRUE else {
        warning("OddNumber: must feed me an ODD number!")
        FALSE
    }
} %as% {
    x
}

But this currently won't work:

Error in parse_guard(it) : Invalid symbol '{'in function definition

Vectorize pattern matching.

During the discussion about vectorizing the lambda.tools function onlyif(condition, fn, x) when length(condition) == length(x), we decided that vectorization can be implemented via the vectorization of pattern matching in lambda.r.

Guards for types

Please delete! I misunderstood some things :-) Thanks for the excellent library!

Converting from type A to type B creates third type (B/A)

Hi. Not sure if this is a bug or a misinterpretation, on my part, of how the package is meant to be used...

How would I get the return value of 'to_sample' (below) to be of type Sample/Whole/numeric -- which is what I expected/hoped for -- rather than Sample/Milliseconds/Whole/numeric (see testthat results pasted below)?

Thanks.

Code

Whole(n) %::% numeric : numeric
Whole(n) %when% {
  n >= 0
} %:=% {
  floor(n)
}


Natural(n) %::% numeric : numeric
Natural(n) %when% {
  is.count(n)
} %:=% {
  n
}


Sample(n) %::% numeric : numeric
Sample(n) %:=%
  Whole(n)


Milliseconds(n) %::% numeric : numeric
Milliseconds(n) %:=%
  Whole(n)


SamplePerSecond(n) %::% numeric : numeric
SamplesPerSecond(n) %:=%
  Natural(n)


to_sample(ms, sample_per_s) %::% Milliseconds : SamplesPerSecond : Sample
to_sample(ms, sample_per_s) %as% {
  per_ms <-
    0.001

  floor(ms * sample_per_s * per_ms) %>% Sample()
}

Failing test result

> library("testthat")
>
> context("to_sample")
> 
> thousand_per_second <-
+   SamplesPerSecond(1000)
> 
> 
> test_that("zero samples can be found in zero milliseconds", {
+   sample_duration <-
+     Milliseconds(0)
+ 
+   to_sample(sample_duration, thousand_per_second) %>%
+     expect_equal(Sample(0))
+ })

Error: Test failed: 'zero samples can be found in zero milliseconds'

  • . not equal to Sample(0).
    Classes differ: Sample/Milliseconds/Whole/numeric is not Sample/Whole/numeric

StatET cannot index lambda.r functions

I am a user of StatET (I assume a lot of other users) and unfortunately there is no way for StatET to index a defition of a function defined by lambda.r;
Are there any plans to make lambda.r compatible with R syntax? Are there any IDE which support lambda.r syntax?

Doesn't accept `call` as a function type parameter

EvalCall(quote_call) %::% call : numeric
EvalCall(quote_call) %as% {
    eval(quote_call)
}

EvalCall_Native = function(quote_call){ eval(quote_call) }
> EvalCall(quote({2+2}))
Error in UseFunction(type.fn, type.name, ...) : 
  No valid function for 'EvalCall({)'

For native:

> EvalCall_Native(quote({2+2}))
[1] 4

Pipe operator bug

Noticed a problem with %as% in combination with %>%:

R version 4.1.0 (2021-05-18)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Ubuntu 20.04.2 LTS

Matrix products: default
BLAS:   /usr/lib/x86_64-linux-gnu/blas/libblas.so.3.9.0
LAPACK: /usr/lib/x86_64-linux-gnu/lapack/liblapack.so.3.9.0

locale:
 [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C               LC_TIME=en_US.UTF-8       
 [4] LC_COLLATE=en_US.UTF-8     LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8   
 [7] LC_PAPER=en_US.UTF-8       LC_NAME=C                  LC_ADDRESS=C              
[10] LC_TELEPHONE=C             LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] lambda.r_1.2.4

loaded via a namespace (and not attached):
[1] compiler_4.1.0 formatR_1.10   tools_4.1.0  
testf() %as% {unique(2)}
testf() 
[1] 2
testf() %as% {2 %>% unique()}
testf()
 Error in 2 %>       
%: 
  could not find function "%>         
%"

Please note the problematic line break.

Shorter lambdas

I was inspired by this library and by my experience in functional programming in general to try to hack up a shorter lambda syntax for R. %as% didn't work in blocks (with lapply etc). Here's what I came up with, inspired by Haskell and Clojure. It's pretty ugly, but it is a nice proof of concept. Wonder if it would be possible to use defmacro or strmacro to supply the function as a {} block instead of a string... Something for a future lambda.r, if we could make it cleaner/saner?

https://gist.github.com/houshuang/6ff2cb250a91d94ae70f

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.