Giter VIP home page Giter VIP logo

doubt's People

Contributors

moodymudskipper avatar

Stargazers

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

Watchers

 avatar  avatar  avatar

doubt's Issues

About syntax consistency, custom operators and n-ary operators

We should strive to minimize the confusion created by this package, this can be done by having strong consistency and no ambiguity.

Allowing the question mark to be anywhere in the operator (start, end, middle) can create ambiguity and confusion even in unambiguous cases.

Maybe custom operators should be disabled, just define a function and use the associated dubious syntax.

We could maybe still allow custom operators using other operators and the dot ? !.?, ?! etc ? And their precedence would be either:

  • the one of the first operator
  • ?'s precedence
  • chosen

?foo? is confusing and doesn't look good, one idea was to have ?foo: instead, which looks a bit like "console input", but it's ambiguous because of bar + baz ?foo: qux.

One more things against ?foo? is that its precedence is not intuitive, as ?syntax is wrong, so baz= bar ?foo? qux is parsed differently from baz<- bar ?foo? qux, so a question can be : do we really need it at all ? ~foo? already has quite low precedence.

Another confusing feature is n-ary operators, the current proposal uses a lot of question marks and its implementation is not robust (though fixable in principle).

Maybe the n-ary syntax is not necessary and we can get what we need from { syntax, the following doesn't look too bad :

~foo?{
  arg1
  arg2
}

arg1 ~foo?{
  arg2
  arg3
}
   

The example of dubioustests : https://github.com/moodymudskipper/dubioustests

For dubious tests, defining ?~ is useful, but things like ?e~ tan(pi/4) ? 1 might not be that useful, we can just do tan(pi/4) ~expect_equal? 1 here.

info on positions of arguments

When the function name is matched we could check if the function has a n_left argument, if it has, the value of the number of arguments on the left should be passed to it.

Then it will be easy to have functions restricting their behavior to be purely infix or impose to have args on right hand side, or handle differently situation depending on where the arguments are provided:

foo <- function(x , y, n_left = 1){
  if(n_left != 1) stop("foo() requires `n_left == 1`, did you use the infix form improperly ?)
  ...
} 

We could even imagine that ? checks foo's formals and returns the error there if defaults are given.

N-ary ops

These could be made possible, see :

https://twitter.com/antoine_fabri/status/1161571033503227906

The number of ?, once the main operator extracted, would determine the number of parameter.

So probably we'll have x ?foo? y equivalent to ?foo? x ? y if we want to enable operators by a regular function definition as we do now (which is convenient).

Should we also have ?bar? x ? y ? z equivalent to x ?bar? y ? z, and even maybe x ? y ?bar? z ? or should we decide that arguments to N-ary ops always go to the rhs ?


We could also have special assignment ops in doubt that register the function (if we take that route), and optionally give attributes to our function :

`?foo?` ?<-? function(e1, e2) {...}
?foo?` ?<-? lhs = FALSE ? function(e1, e2) {...}
?foo?` ?<-?
  lhs = FALSE ? 
  precedence = `*` ?
  function(e1, e2) {...}

We could also add specifications as arguments of the function, they would be parsed by doubt::`?`() but not used.

`?foo?` <- function(e1, e2, lhs = FALSE){...}

to register we can also use := with ? (?<- can't be used because x should exist).

?  `?foo?` := function(e1,e2) {...}

All of this fun things aside maybe it is simpler to keep it as it is, making the syntax flexible by default, and then call functions on top (maybe find better names) :

set_sides("?foo?", left = 0)

by default left and right are NULL which means flexible, else value gives the number of args on each side (only one value is needed). Alternately we'd feed argument names to left or right, that would allow us to decide where the ... go.

We could then also have :

set_precedence("?foo?", "*")

A final idea, we can name arguments according to which side they should go to

`?foo?` <- function(x,y1, y2) {...}

would allow only `a ?foo? b ? c

... would then always go on the right, and if we register operators we'd just us ?<-?

should we force custom ops to be consistent with `<op>fun?` ops ?

it can be frustrating not to be able to assign a precedence, usually we can define fun first and then call <op>fun? but what if :

  • we don't want to make fun available for all <op>fun? forms ? as in dubioustests where we'd rather propose the ~fun? form only
  • we don't want to create a function fun because it would never be called by itself and would be confusing as it would have its help page etc...

This will make sure that we don't define a ~fun? operator that has a precedence different from ~, so will be more intuitive overall.

support multiple operators for `<op>foo?`

Its is possible without ambiguity thanks to the regex that is used, I'm not sure if it can be done using externally defined ops.

OTOH I'm also starting to doubt if we need the latter at all, just that being able to define ?! and such is neat.

have dubious aliases for any function through special syntax ?

We could have for example either the syntax?foo?, ?foo> or ?foo:
and make it an alias for foo

This way right out of the box these would be equivalent :

?paste: a ? b ? c
paste(a,b,c)

?paste: 
  a ? 
  b ? 
  c
paste(
  a,
  b,
  c)

?foo? looks nicer for binary but ?foo> and ?foo: look nicer for unary
if we manage to implement precedence and side restrictions, we could have
several syntaxes. Precedence could actually be given by the last character so
we'd have ?paste+ or ?+paste+ or ?paste+? or ?+paste+? have the precedence of +, and ?%paste% or
?%paste%? have the precedence of %%

Benefits would be :

  • out of the box infix operators for every function
  • out of the box unary operations without brackets, such as ?shell> "dir"
  • we can just define regular function foo <- function(...) ... rather than
    `` ?foo? <- function(...) ... ` which will make it much more intuitive for
    less advanced users
  • no operator registration necessary

Note that to work with assignments, setting the precedence is necessary because
? has lower precedence than assignment operators (an alternative would be to
disentangle assignment ops from the argument but it's messy and not robust)

We would keep the option to build custom ops not following standard dubious syntaxes
and we would either leave the option to override standard syntax, or just reserve it,
right now I think allowing (but maybe discouraging ?) overriding would be the best.

control flow ?

would be fun to have control flow equivalent :

?IF? 2*2 == 4 ? "yes" ? "no"
?FOR? i ? 1:3 ? print(2*i)
?{}? 
  x <- 2 ? 
  y <- 3 ? 
  print(x*y) 

note : cannot use ?for? or ?if? as the parser doesn't like it

?foo:

Implement ?foo: which is like ?foo? but fails if given a lhs

use `~~` rather than `+` for parsing

unary + can be used, I ran into problems trying to pipe to +ggplot(...), which is necessary when using a lower precedence pipe such as ~.?.

if we use ~ instead of + and make sure to use at least 2 ~~ characters we should be fine, and we can fail explicitly when source expression contains "~~"

Preparing release

General checklist, tick if done OR irrelevant

Package design :

  • limit dependencies (better for regular users but also for reverse dependencies)
  • if dependencies are needed, strive to choose mainstream ones (users will be more familiar, and won't need to download them)
  • if dependencies are needed, strive to choose light ones (avoid dependencies with dependencies)
  • Use mainstream testing workflow and strive for 100% test coverage
  • Have tests and code readable/understandable to users
  • Test on different systems and different IDEs

Documentation + online presence:

  • Good file DESCRIPTION
  • Don't forget github description
  • Proper release
  • README
  • vignette(s)
  • use cases
  • benchmarks
  • pkgdown website / author website
  • Have an attached peer reviewed publication
  • document alternative choices to showcase why/when choosing our is appropriate
  • Submit to CRAN task view https://cran.r-project.org/web/views/

Advertising package (similar to "online" presence above, but with a timestamp):

github Badges :

  • CRAN badge
  • covr badge
  • cranlogs badge
  • travis badge

Activity indicators :

  • Push regularly enough to CRAN if improvements have been made (gives the best to users AND shows the package is still maintained)
  • Prompt responses to bug reports and feature requests

Things that are outside of our direct control :

  • Author reputation/contributions
  • github stars
  • CRAN downloads
  • reverse dependencies
  • commit frequency (not technically outside our control, but not going to do it for no reason)

fix `?` when curly braces

Hopefully this is good enough

txt <- paste(rlang::expr_deparse(call), collapse = "\n")

rather than

txt <- rlang::expr_deparse(call)

issue with backticks

This fails :

`y-z` <- 1
+is.numeric? `y-z`

Error in eval(expr, p) : object 'y' not found

could `?` know where it is and act accordingly ?

not sure how it would play out but it would help with this kind of things :

library(dplyr,warn.conflicts = FALSE)
#> Warning: package 'dplyr' was built under R version 3.6.1
`?` <- function(fun) {
  vars <- eval.parent(quote(tidyselect::peek_vars()))
  data <- eval.parent(quote(.))
  vars_lgl <- purrr::map_lgl(vars, ~fun(data[[.]]))
  which(vars_lgl)
}
starwars %>% head(2) %>% select(?is.numeric, name) 
#> # A tibble: 2 x 4
#>   height  mass birth_year name          
#>    <int> <dbl>      <dbl> <chr>         
#> 1    172    77         19 Luke Skywalker
#> 2    167    75        112 C-3PO

Created on 2019-08-21 by the reprex package (v0.3.0)

Here we could design a specific dubious operator but it's more character and it would be an operator that makes sense only in a specific context.

Instead, ? could know where it is from sys.call(), and then be mapped to the relevant behavior.

The above could be defined as ?_select, which reminds of the S3 method syntax.

We'd have it as a fallback behavior, first try to find a dubious op, if not try to find a dubious method, if not map to utils::'?'

Not sure what kind of use cases we'd have apart from the one above.

use custom pipe rather than magrittr

we can copy %>>% from fastpipe and then we get dependency free piping in doubt, which will allow things like the following:

# to define in the function or in the package
. <- function (lhs, rhs) 
  eval(substitute(rhs), envir = list(. = lhs), enclos = parent.frame())
iris ~.?
  dim(.) + 3 ~.?
  log(.)
#> [1] 1.945910 2.079442

control flow ?

Not all that useful but interesting ? can be used to do things that look like control flow :

`?` <- quote
?FOR: {
 
}:step:{

}:do:{
   
}

?parallel: {
 job1 
}:{
 job2
}:{
 job3 
}

parallel({
  job1 
},{
  job2
},{
  job3 
})


?FOR: x :IN: v :{
  
}

Might be a fun idea for a POC package to try to reproduce all the control flows that R doesn't feature directly, and add the parallel one, maybe make ?IF:{ }:ELSE:{ } for ifelse, for loops with a progress bar, foreach...

Probably not worth coding into doubt, can be made by defining custom functions, and our POC package would have an utility to build those.

support packaged operators

right now doubt only looks in the parent environment for an operator to use so this is not very useful as operators will often not be found.

We could have a mypackage.doubt.ops option, and doubt would look in all options of the format "^.*?\\.doubt\\.ops$ to find which operator to apply.

In the latter case we'd also have a doubt.ops set .onLoad of doubt, which will register some predefined operators, such as ?suppressWarnings? or such.

Operators defined in the global environment and parent environments in the local environment should be checked as well, and a priority in case of conflict should be defined (local first then parents then global then packages.

Maybe doubt could have a hook so that when any package is loaded its workspace is explored to find a ^\\? operator, which is registered in doubt, when doubt is first loaded it will register all that exists.

The problem is that listing objects in local + parents all the time to look for a ^\\? object can be slow, this could be the default and then we have an option to use disable local definitions and keep only packaged versions (and maybe global ?), but this is a bit weird too. We can also force registration for all.

expand possible syntax for unary ops

At the moment we cannot have foo? operator, while they would make good unary ops.

We can even have unary ops working on the rhs : ?foo

it will always look a lot like piping in a function. but we can have :

  • ?p to print
  • ?v to view
  • ?e to edit

etc

Problem is it might conflict with standard help, so maybe use a name like ~?p (will catch everything except the assignment) or ?~p or ?!p.

The latter looks quite good!

`?foo> x < y` is not syntactic

It seems that the logic that > is for comparison is built into the parser, so x > y > z doesn't work.

Is it enough to change our console operators ?

These work :

?foo> {x < y}
?foo$ x < y
?foo: x < y

It is rare enough to have a comparison alone line on a line or on the lhs. On the other hand it's puzzling to debug so if we can choose we might as well avoid it.

We'd be more flexible if custom syntaxes took input as strings

See this example works :

library(doubt)
`def?{nm}({arg}):{expr}` <- function(nm, arg, expr){
  eval.parent(substitute(nm <- as.function(c(setNames(alist(x=), deparse(quote(arg))), quote(expr)))), 4) # n = 4 to be fixed in other issue
  invisible()
}

def ? add_2(foo):{foo + 2}
add_2(3)
#> [1] 5

but we can't give a default value to foo, and we can't use an arbitrary number of arguments in () because we parse the content.

If we keep these inputs as string and do the parsing in the function we'll be more flexible.

desired behavior :

`def?{nm}({args}):{expr}` <- function(nm, args, expr){
  eval.parent(str2lang(glue::glue("{nm} <- function({args}) {expr}")))
  # doubt_eval("{nm} <- function({args}) {expr}") # maybe we can have a helper function
  invisible()
}

algorithm to implement precedence

We don't assign manually a precedence to an operator, the precedence is deduced from its name :

  • +foo? (etc) for binary operators (?foo+ will have unary precedence when used as unary)
  • %foo%? for infix operators
  • !foo? for ! which is unary
  • <-foo? for assignments

I first thought of starting with the question mark but with the unary/binary issue and the parsing issue with <- it seems more consistent to have ? on the rhs (this leaves all options open for the console variants so we wight go for the nice and robust ?foo:).

  • The expression will be parsed and fed to rlang::expr_deparse
  • If unary + are spotted (they have no space on rhs when parsed, fail and
  • The relevant dubious op is matched
  • Its precedence is deduced from its shape, if ?, behave as now, else :
  • in the string the "<dubious_op>" is replaced by "<precedence_op>+"
  • the string is parsed, so we get the appropriate parse tree
  • the parse tree is explored and when an unary + is found (+ with a single argument) it's parent is replaced by the dubious op's value, and unary + is cut out of the branch
  • then the exploration should still continue on given branch, in case the op is there several times
  • expression is evaluated in parent

Tricky part is in bold

Wish: make dubious macros accessible anywhere in the search path

Right now I have a bunch of dubious macros that are cluttering my globalenv. rm(list=ls()) wipes them out and I had to write a wrapper for rm() to skip those that has a ? in their name.

Its a wish to do something like:

dub_macros <- list(`?add: ({x})({y})` = "{x} + {y}")
attach(dub_macros)
?add: (2)(3)

I would look into it myself, but this whole package seems to involve too much magic...

custom syntax is not evaluated in correct environment

See below the syntax of a python style definition, we need to use n = 4 :

library(doubt)
#> 
#> Attaching package: 'doubt'
#> The following object is masked from 'package:utils':
#> 
#>     ?
`def?{nm}({arg}):{expr}` <- function(nm, arg, expr){
  eval.parent(substitute(nm <- as.function(c(setNames(alist(x=), deparse(quote(arg))), quote(expr)))), 4)
  invisible()
}

def ? add_2(foo):{foo + 2}
add_2(3)
#> [1] 5

This is counter-intuitive and probably brittle

speed

I don't know if we can really make it fast but we might think of improvements, maybe parsing with C++ is faster.

Also if we need to find all available "dubious" ops, a naive way would be unique(unlist(lapply(search(),ls, pattern = "^\\?"))) , but it is too slow, we could implement it to move forward, but registering them seems to be the only reasonable option.

We'll need to do some profiling as I have no clue what is fast or slow, I thought parsing was slow but I saw that data.table was doing it a lot so I'm confused.

generalize `?foo?`

whenever ?foo? is detected we can detect a defined pattern and have any n-ary operation in a way that is cleaner AND more flexible than current implementation :

`?` <- function(e1, e2){
  call <- sys.call()
  txt <- doubt:::deparse_rec(call)
  ops <- ls(pattern = "\\?[a-z]+\\?", envir = .GlobalEnv)
  ops_compact <- gsub(" ", "", ops)
  ops_lgl <- sapply(ops_compact, unglue::unglue_detect, x = txt)
  if(!any(ops_lgl)) eval.parent(substitute(utils::`?`(e1,e2)))
  if(sum(ops_lgl) > 1) stop("ambiguous!")
    args <- unglue::unglue(txt, ops_compact[ops_lgl])[[1]]
  eval(as.call(parse(text=c(paste0("`",ops[ops_lgl], "`"), args))))
}

`?add? {x} ?to? {y}` <- function(x,y){
  x+y
}

?add? 2 ?to? 3
#> [1] 5


# we can use any legal syntax, not only `?`
`?add? {x} : {y}` <- function(x,y){
  x+y
}

?add? 2 : 3
#> [1] 5


# We can define control flows too for instance
`?FOR? {x} := {from} :step: {by} :to: {to} :do: {expr}` <- function(x, from, by, to, expr){
  eval.parent(substitute(
    for(x in seq(from, to, by)) expr
  ))
}

?FOR? z := 3 :step: 2 :to: 10 :do: print(z*10) 
#> [1] 30
#> [1] 50
#> [1] 70
#> [1] 90

`?IF? {test} ?THEN? {yes} ?ELSE? {no}` <- ifelse
?IF? 1:5>3 ?THEN? 1 ?ELSE? 0
#> [1] 1 1 1 0 0

`{con} ?SELECT? {query}` <- function(con, query){
  query <- paste("SELECT", deparse(substitute(query)))
  query <- gsub("?","", query)
  query <- gsub("`","", query)
  query <- gsub("\\%(.+?)\\%", "\\1", query)
  query <- gsub("%%", "%", query, fixed = TRUE)
  DBI::dbGetQuery(con, query)
}

db ?SELECT? `*` %FROM% mytbl %LIMIT% 10 

support devtools_shims 's `?`

? when redirected to standard use should look if devtool_shim is attached and use it if it is, or just go up the search pah looking for ?.

The latter is probably better, I think it would make the ??? of package sos work etc.

Now we just redirect to the utils version.

Improvements

Precedence could be decided if instead of replacing operator by ? we replace it by op+ , for example ==+, the definition of == in this case will be altered to be different only when an unary + follows it.

Precedence can be chosen until unary + and not higher.

Operator doesn't have to start with ? . For instance +.?.+ could be defined as ? is binary here and views all. Same for <-.?

Other operators can be overloaded too but shouldn't by default. And their precedence will be more restricted. For ex <.|.

utils::`?` is not called anymore

I must have messed up something somewhere.

While I'm at it, check if package SOS is installed, and if it is, then call sos::`?` , so its users don't use it.

We can also plug ??? on sos::`?` and it should work all the same but maybe we want to aim at having only one exported function in doubt when we're done having fun with experiments ?

Use glue for more flexibility

atm we can only use symbols in {}, as a result it would be much harder to do the following:

library(doubt, warn.conflicts = FALSE)
library(dplyr, warn.conflicts = FALSE)
# fix call
body(`?`)[[9]][[3]][[6]] <- quote(call_chr <- glue::glue(val, .envir = args))

"? {df} ? {expr} ~ {by}" <- "
{df} %>% 
  group_by({gsub('\\\\+', ',', by)}) %>% 
  summarize({gsub('\\\\?', ',', expr)}, .groups = 'drop')"

?mtcars? mean_drat = mean(drat) ? max_disp = max(disp)  ~ vs + am
#> # A tibble: 4 x 4
#>      vs    am mean_drat max_disp
#>   <dbl> <dbl>     <dbl>    <dbl>
#> 1     0     0      3.12      472
#> 2     0     1      3.94      351
#> 3     1     0      3.57      258
#> 4     1     1      4.15      121

tidy equivalent :

mtcars %>% 
  group_by(vs, am) %>% 
  summarize(mean_drat = mean(drat), max_disp = max(disp), .groups = 'drop')

grViz and DOT language !!!

grViz/ DOT code is mostly syntactic in R, enough so that the example advertised on their website works perfectly here, and so do all of the examples from wikipedia and random examples from online galleries.

I just had to add a coma where it was optional in grViz but mandatory in R, and use ++ in the rare instances where a non syntactic space is required. It detects automatically if we want a graph or a digraph.

See : https://en.wikipedia.org/wiki/DOT_(graph_description_language)

It's a cool use case because we gain indentation and syntax highlighting and lose double quotes and a closing bracket for the price of nothing.

? need to be fixed first, see 1st line

# current version is not friendly with braces, quick fix
trace(`?`, quote(txt <- paste(rlang::expr_deparse(call),collapse = "\n")),at = 4)

`?grViz>` <- function (expr) {
  if (!requireNamespace("DiagrammeR")) 
    stop("Install package DiagrammeR to use `?grViz>` function")
  
  expr <- substitute(expr)
  if (is.character(expr)) {
    code <- expr
  } else {
    if(expr[[1]] != quote(`{`)) stop("The expression must be wrapped in {braces}")
    expr[[1]] <- NULL
    code <- as.character(expr)
    code <- sapply(code, function(x) paste(rev(strsplit(x,"<-")[[1]]), collapse = " -> "))
    # code <- gsub("^(.*?)<-(.*?)$", "\\2 -> \\1", code)
    code <- gsub(" \\+ \\+", " ", code)
    code <- gsub(" - -", " -- ", code)
    code <- paste(code, collapse = "\n")
  }
  if(grepl("-\\s?-",code)){
    if(grepl("<-",code))
      stop("You can't use both `--` (graph) and `->` (digraph)")
    g <- "graph"
  } else {
    g <- "digraph"
  }
  
  code <- paste0(g, "{", code, "}")
  DiagrammeR::grViz(code)
}


?grViz> {
  # add node statement
  node[shape = circle, 
       fontname = Helvetica,
       penwidth = 2.0]
  A; B; C; D; E; F
  
  node[shape = box]
  1; 2; 3; 4; 5; 6; 7; 8
  
  # add edge statement
  edge[arrowhead = diamond]
  A->1; B->2; B->3; B->4; C->A;
  1->D; E->A; 2->4; 1->5; 1->F;
  E->6; 4->6; 5->7; 6->7;
  3->8 [label = " label!",
        fontname = Helvetica]
  # add a graph statement
  graph[nodesep = 0.1]
  
  # Enjoy using DiagrammeR... It's fun!
}



?grViz> {
  a -- b -- c;
  b -- d;
}

?grViz>{
    a -> b -> c;
    b -> d;
  }

?grViz> {
  # This attribute applies to the graph itself
  size="1,1";
  # The label attribute can be used to change the label of a node
  a [label="Foo"];
  # Here, the node shape is changed.
  b [shape=box];
  # These edges both have different line properties
  a -- b -- c [color=blue];
  b -- d [style=dotted];
  # [style=invis] hides a node.
}

?grViz> {
  C_0 -- H_0 [type=s];
  C_0 -- H_1 [type=s];
  C_0 -- H_2 [type=s];
  C_0 -- C_1 [type=s];
  C_1 -- H_3 [type=s];
  C_1 -- H_4 [type=s];
  C_1 -- H_5 [type=s];
}

?grViz> {
  node [shape=plaintext];
  A1 -> B1;
  A2 -> B2;
  A3 -> B3;
  
  A1 -> A2 [label=f];
  A2 -> A3 [label=g];
  B2 -> B3 [label="g"];
  B1 -> B3 [label="(g o f)", tailport=s, headport=s];
  
  { rank=same; A1 ++ A2 ++ A3 }
  { rank=same; B1 ++ B2 ++ B3 } 
}


?grViz> {
  #   node [fontsize=10,width=".2", height=".2", margin=0];
  #   graph[fontsize=8];
  label="((+ (* (X) (- (- (X) (X)) (X))) (% (+ (X) (X)) (COS (- (X) (X))))) (EXP (* (X) (X))) (+ (% (EXP (SIN (+ (X) (X)))) (SIN (* (X) (EXP (* (X) (X)))))) (* (X) (X))) (% (EXP (% (X) (% (X) (X)))) (EXP (SIN (X)))))"
  
  subgraph ++ cluster01
  {
    label="(+ (* (X) (- (- (X) (X)) (X))) (% (+ (X) (X)) (COS (- (X) (X)))))"
    n002 ;
    n002 [label="+"] ;
    n002 -- n003 ;
    n003 [label="*"] ;
    n003 -- n004 ;
    n004 [label="X"] ;
    n003 -- n005 ;
    n005 [label="-"] ;
    n005 -- n006 ;
    n006 [label="-"] ;
    n006 -- n007 ;
    n007 [label="X"] ;
    n006 -- n008 ;
    n008 [label="X"] ;
    n005 -- n009 ;
    n009 [label="X"] ;
    n002 -- n010 ;
    n010 [label="%"] ;
    n010 -- n011 ;
    n011 [label="+"] ;
    n011 -- n012 ;
    n012 [label="X"] ;
    n011 -- n013 ;
    n013 [label="X"] ;
    n010 -- n014 ;
    n014 [label="COS"] ;
    n014 -- n015 ;
    n015 [label="-"] ;
    n015 -- n016 ;
    n016 [label="X"] ;
    n015 -- n017 ;
    n017 [label="X"] ;
  }
  
  subgraph ++ cluster17
  {
    label="(EXP (* (X) (X)))"
    n018 ;
    n018 [label="EXP"] ;
    n018 -- n019 ;
    n019 [label="*"] ;
    n019 -- n020 ;
    n020 [label="X"] ;
    n019 -- n021 ;
    n021 [label="X"] ;
  }
  
  subgraph ++ cluster21
  {
    label="(+ (% (EXP (SIN (+ (X) (X)))) (SIN (* (X) (EXP (* (X) (X)))))) (* (X) (X)))"
    n022 ;
    n022 [label="+"] ;
    n022 -- n023 ;
    n023 [label="%"] ;
    n023 -- n024 ;
    n024 [label="EXP"] ;
    n024 -- n025 ;
    n025 [label="SIN"] ;
    n025 -- n026 ;
    n026 [label="+"] ;
    n026 -- n027 ;
    n027 [label="X"] ;
    n026 -- n028 ;
    n028 [label="X"] ;
    n023 -- n029 ;
    n029 [label="SIN"] ;
    n029 -- n030 ;
    n030 [label="*"] ;
    n030 -- n031 ;
    n031 [label="X"] ;
    n030 -- n032 ;
    n032 [label="EXP"] ;
    n032 -- n033 ;
    n033 [label="*"] ;
    n033 -- n034 ;
    n034 [label="X"] ;
    n033 -- n035 ;
    n035 [label="X"] ;
    n022 -- n036 ;
    n036 [label="*"] ;
    n036 -- n037 ;
    n037 [label="X"] ;
    n036 -- n038 ;
    n038 [label="X"] ;
  }
  
  subgraph ++ cluster38
  {
    label="(% (EXP (% (X) (% (X) (X)))) (EXP (SIN (X))))"
    n039 ;
    n039 [label="%"] ;
    n039 -- n040 ;
    n040 [label="EXP"] ;
    n040 -- n041 ;
    n041 [label="%"] ;
    n041 -- n042 ;
    n042 [label="X"] ;
    n041 -- n043 ;
    n043 [label="%"] ;
    n043 -- n044 ;
    n044 [label="X"] ;
    n043 -- n045 ;
    n045 [label="X"] ;
    n039 -- n046 ;
    n046 [label="EXP"] ;
    n046 -- n047 ;
    n047 [label="SIN"] ;
    n047 -- n048 ;
    n048 [label="X"] ;
  }
}

?grViz> {
  I5 [shape=ellipse,color=red,style=bold,label="Caroline Bouvier Kennedy\nb. 27.11.1957 New York",image="images/165px-Caroline_Kennedy.jpg",labelloc=b];
  I1 [shape=box,color=blue,style=bold,label="John Fitzgerald Kennedy\nb. 29.5.1917 Brookline\nd. 22.11.1963 Dallas",image="images/kennedyface.jpg",labelloc=b];
  I6 [shape=box,color=blue,style=bold,label="John Fitzgerald Kennedy\nb. 25.11.1960 Washington\nd. 16.7.1999 over the Atlantic Ocean, near Aquinnah, MA, USA",image="images/180px-JFKJr2.jpg",labelloc=b];
  I7 [shape=box,color=blue,style=bold,label="Patrick Bouvier Kennedy\nb. 7.8.1963\nd. 9.8.1963"];
  I2 [shape=ellipse,color=red,style=bold,label="Jaqueline Lee Bouvier\nb. 28.7.1929 Southampton\nd. 19.5.1994 New York City",image="images/jacqueline-kennedy-onassis.jpg",labelloc=b];
  I8 [shape=box,color=blue,style=bold,label="Joseph Patrick Kennedy\nb. 6.9.1888 East Boston\nd. 16.11.1969 Hyannis Port",image="images/1025901671.jpg",labelloc=b];
  I10 [shape=box,color=blue,style=bold,label="Joseph Patrick Kennedy Jr\nb. 1915\nd. 1944"];
  I11 [shape=ellipse,color=red,style=bold,label="Rosemary Kennedy\nb. 13.9.1918\nd. 7.1.2005",image="images/rosemary.jpg",labelloc=b];
  I12 [shape=ellipse,color=red,style=bold,label="Kathleen Kennedy\nb. 1920\nd. 1948"];
  I13 [shape=ellipse,color=red,style=bold,label="Eunice Mary Kennedy\nb. 10.7.1921 Brookline"];
  I9 [shape=ellipse,color=red,style=bold,label="Rose Elizabeth Fitzgerald\nb. 22.7.1890 Boston\nd. 22.1.1995 Hyannis Port",image="images/Rose_kennedy.JPG",labelloc=b];
  I15 [shape=box,color=blue,style=bold,label="Aristotle Onassis"];
  I3 [shape=box,color=blue,style=bold,label="John Vernou Bouvier III\nb. 1891\nd. 1957",image="images/BE037819.jpg",labelloc=b];
  I4 [shape=ellipse,color=red,style=bold,label="Janet Norton Lee\nb. 2.10.1877\nd. 3.1.1968",image="images/n48862003257_1275276_1366.jpg",labelloc=b];
  I1 -- I5  [style=bold,color=blue]; 
  I1 -- I6  [style=bold,color=orange]; 
  I2 -- I6  [style=bold,color=orange]; 
  I1 -- I7  [style=bold,color=orange]; 
  I2 -- I7  [style=bold,color=orange]; 
  I1 -- I2  [style=bold,color=violet]; 
  I8 -- I1  [style=bold,color=blue]; 
  I8 -- I10  [style=bold,color=orange]; 
  I9 -- I10  [style=bold,color=orange]; 
  I8 -- I11  [style=bold,color=orange]; 
  I9 -- I11  [style=bold,color=orange]; 
  I8 -- I12  [style=bold,color=orange]; 
  I9 -- I12  [style=bold,color=orange]; 
  I8 -- I13  [style=bold,color=orange]; 
  I9 -- I13  [style=bold,color=orange]; 
  I8 -- I9  [style=bold,color=violet]; 
  I9 -- I1  [style=bold,color=red]; 
  I2 -- I5  [style=bold,color=red]; 
  I2 -- I15  [style=bold,color=violet]; 
  I3 -- I2  [style=bold,color=blue]; 
  I3 -- I4  [style=bold,color=violet]; 
  I4 -- I2  [style=bold,color=red]; 
}

support empty rhs ? pipe with any relevance ?

In some case we might want to be able to feed an empty rhs to fully leverage the precedence flexibility.

We can do it by recognizing a pattern on the rhs as meaning "missing argumen", a good candidate would be {}

So for instance we can do :

ggplot(data, aes(x,y)) + geom_point() +ggplotly? {}

This might not be so useful though if we design a way to pipe with any precedence, which is actually simple.

library(doubt)
#> 
#> Attaching package: 'doubt'
#> The following object is masked from 'package:utils':
#> 
#>     ?
. <- function(lhs, rhs) {
  eval(substitute(lhs %>% rhs), 
       enclos = parent.frame(), 
       envir = list(`%>%` = magrittr::`%>%`))
}

iris +.? head(2)
#>   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
#> 1          5.1         3.5          1.4         0.2  setosa
#> 2          4.9         3.0          1.4         0.2  setosa

Created on 2019-09-13 by the reprex package (v0.3.0)

Then we can easily do

ggplot(data, aes(x,y)) + geom_point() +.? ggplotly()

We'd have several options:

  • define and export the function .()
  • don't export it but manage for our code to look into the package too
  • define it locally in the code and manage to make it work as a special case

In any case our code now uses get0(), that's not good, we should use ´getFunction()´ and existsFunction()

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.