diff --git a/_freeze/conversion/execute-results/html.json b/_freeze/conversion/execute-results/html.json new file mode 100644 index 0000000..b418e29 --- /dev/null +++ b/_freeze/conversion/execute-results/html.json @@ -0,0 +1,15 @@ +{ + "hash": "83a91459805cb87056cd6423ed50e8dd", + "result": { + "engine": "knitr", + "markdown": "---\ntitle: \"Conversion to and from R data\"\n---\n\n\nOne of the key goals with extendr, is to provide a framework that allows you to write Rust functions, that interact with R, without having to know the intricacies within R internals, or even R's C-facilities. However, this is unavoidable if one wishes to understand why the extendr-api is the way it is.\n\nThus, for introducing extendr, we shall mention facts about R internals, but these are not necessary to keep in mind going forward.\n\n\n::: {.cell}\n\n:::\n\n\nA fundamental data-type in R is the 32-bit integer, `int` in C, and `i32` in Rust. Passing that type around is essential, and straight forward:\n\n\n::: {.cell}\n\n```{.rust .cell-code}\n#[extendr(use_try_from = true)]\nfn ultimate_answer() -> i32 {\n return 42_i32;\n}\n```\n:::\n\n\nAnd now this function is available within your R-session, as the output is 42.\n\nAlso, another fundamental data-type in R is `numeric` / `f64`, which we can also pass back and forth uninhibitated, e.g.\n\n\n::: {.cell}\n\n```{.rust .cell-code}\n#[extendr(use_try_from = true)]\nfn return_tau() -> f64 {\n std::f64::consts::TAU\n} \n```\n:::\n\n\nwhere $\\tau := 2\\pi =$ $6.2831853$.\n\nHowever, passing data from R to Rust must be done with a bit of care: In R, representing a true integer in literal form requires using `L` after the literal. \n\n\n::: {.cell}\n\n```{.rust .cell-code}\n#[extendr(use_try_from = true)]\nfn bit_left_shift_once(number: i32) -> i32 {\n number << 1\n}\n```\n:::\n\n\nThis function supposedly is a clever way to multiply by two, however passing `bit_left_shift_once(21.1)` results in\n\n\n::: {.cell}\n::: {.cell-output .cell-output-error}\n\n```\nError in bit_left_shift_once(21.1): Expected an integer or a float representing a whole number, got 21.1\n```\n\n\n:::\n:::\n\nwhere `bit_left_shift_once(21)` is 42, as expected.\n\nR also has the concept of missing numbers, `NA` encoded within its data-model. However `i32`/`f64` do not natively have a representation for `NA` e.g.\n\n\n::: {.cell}\n\n```{.r .cell-code}\nbit_left_shift_once(NA_integer_)\n```\n\n::: {.cell-output .cell-output-error}\n\n```\nError in bit_left_shift_once(NA_integer_): Must not be NA.\n```\n\n\n:::\n\n```{.r .cell-code}\nbit_left_shift_once(NA_real_)\n```\n\n::: {.cell-output .cell-output-error}\n\n```\nError in bit_left_shift_once(NA_real_): Must not be NA.\n```\n\n\n:::\n\n```{.r .cell-code}\nbit_left_shift_once(NA)\n```\n\n::: {.cell-output .cell-output-error}\n\n```\nError in bit_left_shift_once(NA): Must not be NA.\n```\n\n\n:::\n:::\n\n\nInstead, we have to rely on extendr's scalar variants of R types, `Rint` / `Rfloat` to encompass the notion of `NA` in our functions:\n\n\n::: {.cell}\n\n```{.rust .cell-code}\n#[extendr(use_try_from = true)]\nfn double_me(value: Rint) -> Rint {\n if value.is_na() {\n Rint::na()\n } else {\n (value.inner() << 1).into()\n }\n}\n```\n:::\n\nwhich means, we can now handle missing values in the arguments\n\n\n::: {.cell}\n\n```{.r .cell-code}\ndouble_me(NA_integer_)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n[1] NA\n```\n\n\n:::\n\n```{.r .cell-code}\ndouble_me(NA_real_)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n[1] NA\n```\n\n\n:::\n\n```{.r .cell-code}\ndouble_me(NA)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n[1] NA\n```\n\n\n:::\n:::\n\n\nOne may notice here that `NA_real_` was accepted even for an `Rint`. The reason\nfor this, is when you specify a type without `&`/`&mut`, the value is coerced\nin a similar way, as R coerces values. In order to have strict type-checking\nduring run-time, use `&` / `&mut`, as\n\n\n\n::: {.cell}\n\n```{.rust .cell-code}\n#[extendr(use_try_from = true)]\nfn wrong_input(value: &Rint) -> Rint {\n value.clone()\n}\n```\n:::\n\n::: {.cell}\n\n```{.r .cell-code}\nwrong_input(NA_integer_)\n```\n\n::: {.cell-output .cell-output-error}\n\n```\nError in wrong_input(NA_integer_): Must not be NA.\n```\n\n\n:::\n\n```{.r .cell-code}\nwrong_input(NA_real_)\n```\n\n::: {.cell-output .cell-output-error}\n\n```\nError in wrong_input(NA_real_): expected 13, got 14\n```\n\n\n:::\n\n```{.r .cell-code}\nwrong_input(21.0)\n```\n\n::: {.cell-output .cell-output-error}\n\n```\nError in wrong_input(21): expected 13, got 14\n```\n\n\n:::\n\n```{.r .cell-code}\nwrong_input(21L)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n[1] 21\n```\n\n\n:::\n:::\n\n\nHere, only the last literal is a true `Rint`. \n\n## Vectors\n\nMost data in R are vectors. Scalar values are in fact 1-sized vectors, and\neven lists are defined by a vector-type. A vector type in Rust is `Vec`. A\n`Vec` has a type-information, length, and capacity. This means, that if necessary,\nwe may expand any given `Vec`-data to contain more values, and only when capacity\nis exceeded, will there be a reallocation.\n\nNaively, we may define a function like so\n\n\n::: {.cell}\n\n```{.rust .cell-code}\n#[extendr(use_try_from = true)]\nfn repeat_us(mut values: Vec) -> Vec {\n assert_eq!(values.capacity(), values.len(), \"must have zero capacity left\");\n values[0] = 100;\n values.push(55);\n values\n}\n```\n:::\n\n::: {.cell}\n\n```{.r .cell-code}\nx <- c(1L, 2L, 33L)\nrepeat_us(x)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n[1] 100 2 33 55\n```\n\n\n:::\n:::\n\n\nEven if the argument is `mut Vec<_>`, what happens is that the R vector gets\nconverted to a Rust owned type, and it is that type that we can modify, and augment, with syncing to the original data.\n\nOf course, a slice e.g. `&[i32]` / `&mut [i32]` could be used instead, and this allows us to modify the original data, i.e.\n\n\n::: {.cell}\n\n```{.rust .cell-code}\n#[extendr(use_try_from = true)]\nfn zero_middle_element(values: &mut [i32]) {\n let len = values.len();\n let middle = len / 2;\n values[middle] = 0;\n}\n```\n:::\n\n::: {.cell}\n\n```{.r .cell-code}\nx <- c(100L, 200L, 300L)\nzero_middle_element(x)\nx\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n[1] 100 0 300\n```\n\n\n:::\n:::\n\n\nThis is great! If we wanted to insert an `NA` in the middle, we would have had to operate on `&mut [Rint]` instead. \n\nA slice is a representation of a sequence of elements that are part of a larger collection. Since they represent only part of a collection (vector, in this case), we cannot add new elements to this. To do so, we have to rely on extendr provided types, that provide a `Vec`-like API to R's vector-types. These are the `Integers`, `Logicals`, `Doubles`, and `Strings` types.\n\n## Strings are special\n\n", + "supporting": [], + "filters": [ + "rmarkdown/pagebreak.lua" + ], + "includes": {}, + "engineDependencies": {}, + "preserve": {}, + "postProcess": true + } +} \ No newline at end of file diff --git a/_freeze/conversion/execute-results/typ.json b/_freeze/conversion/execute-results/typ.json new file mode 100644 index 0000000..8bd5153 --- /dev/null +++ b/_freeze/conversion/execute-results/typ.json @@ -0,0 +1,15 @@ +{ + "hash": "f9505dcfe899fb366467fde237addb75", + "result": { + "engine": "knitr", + "markdown": "---\ntitle: \"Conversion to and from R data\"\nformat: typst\n---\n\n\nOne of the key goals with extendr, is to provide a framework that allows you to write Rust functions, that interact with R, without having to know the intricacies within R internals, or even R's C-facilities. However, this is unavoidable if one wishes to understand why the extendr-api is the way it is.\n\nThus, for introducing extendr, we shall mention facts about R internals, but these are not necessary to keep in mind going forward.\n\nA fundamental data-type in R is the 32-bit integer, `int` in C, and `i32` in Rust. Passing that type around is essential, and straight forward:\n\n\n::: {.cell}\n\n```{.r .cell-code}\nlibrary(rextendr)\nnames(knitr::knit_engines$get())\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n [1] \"awk\" \"bash\" \"coffee\" \"gawk\" \"groovy\" \n [6] \"haskell\" \"lein\" \"mysql\" \"node\" \"octave\" \n[11] \"perl\" \"php\" \"psql\" \"Rscript\" \"ruby\" \n[16] \"sas\" \"scala\" \"sed\" \"sh\" \"stata\" \n[21] \"zsh\" \"asis\" \"asy\" \"block\" \"block2\" \n[26] \"bslib\" \"c\" \"cat\" \"cc\" \"comment\" \n[31] \"css\" \"ditaa\" \"dot\" \"embed\" \"eviews\" \n[36] \"exec\" \"fortran\" \"fortran95\" \"go\" \"highlight\" \n[41] \"js\" \"julia\" \"python\" \"R\" \"Rcpp\" \n[46] \"sass\" \"scss\" \"sql\" \"stan\" \"targets\" \n[51] \"tikz\" \"verbatim\" \"ojs\" \"mermaid\" \"glue\" \n[56] \"glue_sql\" \"gluesql\" \"extendr\" \"extendrsrc\"\n```\n\n\n:::\n:::\n\n::: {.cell}\n\n```{.rust .cell-code}\n#[extendr(use_try_from = true)]\nfn ultimate_answer() -> i32 {\n return 42_i32;\n}\n```\n:::\n\n\nAnd now this function is available within your R-session, as the output is 42.\n\nAlso, another fundamental data-type in R is `numeric` / `f64`, which we can also pass back and forth uninhibitated, e.g.\n\n\n::: {.cell}\n\n```{.rust .cell-code}\n#[extendr]\nfn return_tau() -> f64 {\n std::f64::consts::TAU\n} \n```\n:::\n\n\nwhere $\\tau$\n\n", + "supporting": [], + "filters": [ + "rmarkdown/pagebreak.lua" + ], + "includes": {}, + "engineDependencies": {}, + "preserve": null, + "postProcess": false + } +} \ No newline at end of file diff --git a/_freeze/site_libs/clipboard/clipboard.min.js b/_freeze/site_libs/clipboard/clipboard.min.js new file mode 100644 index 0000000..1103f81 --- /dev/null +++ b/_freeze/site_libs/clipboard/clipboard.min.js @@ -0,0 +1,7 @@ +/*! + * clipboard.js v2.0.11 + * https://clipboardjs.com/ + * + * Licensed MIT © Zeno Rocha + */ +!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.ClipboardJS=e():t.ClipboardJS=e()}(this,function(){return n={686:function(t,e,n){"use strict";n.d(e,{default:function(){return b}});var e=n(279),i=n.n(e),e=n(370),u=n.n(e),e=n(817),r=n.n(e);function c(t){try{return document.execCommand(t)}catch(t){return}}var a=function(t){t=r()(t);return c("cut"),t};function o(t,e){var n,o,t=(n=t,o="rtl"===document.documentElement.getAttribute("dir"),(t=document.createElement("textarea")).style.fontSize="12pt",t.style.border="0",t.style.padding="0",t.style.margin="0",t.style.position="absolute",t.style[o?"right":"left"]="-9999px",o=window.pageYOffset||document.documentElement.scrollTop,t.style.top="".concat(o,"px"),t.setAttribute("readonly",""),t.value=n,t);return e.container.appendChild(t),e=r()(t),c("copy"),t.remove(),e}var f=function(t){var e=1 i32 { + return 42_i32; +} +``` + +And now this function is available within your R-session, as the output is `r ultimate_answer()`. + +Also, another fundamental data-type in R is `numeric` / `f64`, which we can also pass back and forth uninhibitated, e.g. + +```{extendrsrc} +#[extendr(use_try_from = true)] +fn return_tau() -> f64 { + std::f64::consts::TAU +} +``` + +where $\tau := 2\pi =$ $`r return_tau()`$. + +However, passing data from R to Rust must be done with a bit of care: In R, representing a true integer in literal form requires using `L` after the literal. + +```{extendrsrc} +#[extendr(use_try_from = true)] +fn bit_left_shift_once(number: i32) -> i32 { + number << 1 +} +``` + +This function supposedly is a clever way to multiply by two, however passing `bit_left_shift_once(21.1)` results in + +```{r, error=TRUE, echo=FALSE} +bit_left_shift_once(21.1) +``` +where `bit_left_shift_once(21)` is `r bit_left_shift_once(21L)`, as expected. + +R also has the concept of missing numbers, `NA` encoded within its data-model. However `i32`/`f64` do not natively have a representation for `NA` e.g. + +```{r, error=TRUE} +bit_left_shift_once(NA_integer_) +bit_left_shift_once(NA_real_) +bit_left_shift_once(NA) +``` + +Instead, we have to rely on extendr's scalar variants of R types, `Rint` / `Rfloat` to encompass the notion of `NA` in our functions: + +```{extendrsrc} +#[extendr(use_try_from = true)] +fn double_me(value: Rint) -> Rint { + if value.is_na() { + Rint::na() + } else { + (value.inner() << 1).into() + } +} +``` +which means, we can now handle missing values in the arguments + +```{r, error=FALSE} +double_me(NA_integer_) +double_me(NA_real_) +double_me(NA) +``` + +One may notice here that `NA_real_` was accepted even for an `Rint`. The reason +for this, is when you specify a type without `&`/`&mut`, the value is coerced +in a similar way, as R coerces values. In order to have strict type-checking +during run-time, use `&` / `&mut`, as + + +```{extendrsrc} +#[extendr(use_try_from = true)] +fn wrong_input(value: &Rint) -> Rint { + value.clone() +} +``` + +```{r, error=TRUE} +wrong_input(NA_integer_) +wrong_input(NA_real_) +wrong_input(21.0) +wrong_input(21L) +``` + +Here, only the last literal is a true `Rint`. + +## Vectors + +Most data in R are vectors. Scalar values are in fact 1-sized vectors, and +even lists are defined by a vector-type. A vector type in Rust is `Vec`. A +`Vec` has a type-information, length, and capacity. This means, that if necessary, +we may expand any given `Vec`-data to contain more values, and only when capacity +is exceeded, will there be a reallocation. + +Naively, we may define a function like so + +```{extendrsrc} +#[extendr(use_try_from = true)] +fn repeat_us(mut values: Vec) -> Vec { + assert_eq!(values.capacity(), values.len(), "must have zero capacity left"); + values[0] = 100; + values.push(55); + values +} +``` + +```{r, error=TRUE} +x <- c(1L, 2L, 33L) +repeat_us(x) +``` + +Even if the argument is `mut Vec<_>`, what happens is that the R vector gets +converted to a Rust owned type, and it is that type that we can modify, and augment, with syncing to the original data. + +Of course, a slice e.g. `&[i32]` / `&mut [i32]` could be used instead, and this allows us to modify the original data, i.e. + +```{extendrsrc} +#[extendr(use_try_from = true)] +fn zero_middle_element(values: &mut [i32]) { + let len = values.len(); + let middle = len / 2; + values[middle] = 0; +} +``` + +```{r} +x <- c(100L, 200L, 300L) +zero_middle_element(x) +x +``` + +This is great! If we wanted to insert an `NA` in the middle, we would have had to operate on `&mut [Rint]` instead. + +A slice is a representation of a sequence of elements that are part of a larger collection. Since they represent only part of a collection (vector, in this case), we cannot add new elements to this. To do so, we have to rely on extendr provided types, that provide a `Vec`-like API to R's vector-types. These are the `Integers`, `Logicals`, `Doubles`, and `Strings` types. + +## Strings are special +