moodymudskipper / doubt Goto Github PK
View Code? Open in Web Editor NEWEnable operators containing the '?' symbol
License: GNU General Public License v3.0
Enable operators containing the '?' symbol
License: GNU General Public License v3.0
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:
?
's precedence?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.
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.
doubt:::deparse_rec(quote(`*tmp*`+b))
#> [1] "*tmp*+b"
Should be "`*tmp*` + b"
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 ?<-?
we still use strsplit()
which is problematic, this should be adapted and used instead :
I don't think true parsing in this remaining case is possible, or it would be very hard, and possibly slower because of complexity.
so we can have "?{x}" <- "this;{x};that;"
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 :
fun
available for all <op>fun?
forms ? as in dubioustests where we'd rather propose the ~fun?
form onlyfun
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.
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.
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 :
?shell> "dir"
foo <- function(...) ...
rather than?foo?
<- function(...) ... ` which will make it much more intuitive forNote 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.
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
Implement ?foo:
which is like ?foo?
but fails if given a lhs
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 "~~"
General checklist, tick if done OR irrelevant
Package design :
Documentation + online presence:
Advertising package (similar to "online" presence above, but with a timestamp):
github Badges :
Activity indicators :
Things that are outside of our direct control :
Hopefully this is good enough
txt <- paste(rlang::expr_deparse(call), collapse = "\n")
rather than
txt <- rlang::expr_deparse(call)
This fails :
`y-z` <- 1
+is.numeric? `y-z`
Error in eval(expr, p) : object 'y' not found
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.
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
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.
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.
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 editetc
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!
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.
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()
}
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 assignmentsI 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:
).
rlang::expr_deparse
?
, behave as now, else :+
with a single argument) it's parent is replaced by the dubious op's value, and unary + is cut out of the branchTricky part is in bold
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...
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
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.
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
?
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.
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 <.|
.
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 ?
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
/ 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];
}
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:
.()
In any case our code now uses get0()
, that's not good, we should use ´getFunction()´ and existsFunction()
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.