Skip to content

Instantly share code, notes, and snippets.

Avatar

JT jntrnr

View GitHub Profile
@jntrnr
jntrnr / a case for nushell laziness.md
Last active Jun 27, 2022
A case for Nushell laziness
View a case for nushell laziness.md

A case for Nushell laziness

Up to this point, Nushell has been built around the idea that commands stream data from one to the next via a pipeline. This pipeline is implemented as an iterator in Rust, allowing it to lazily compute the output as needed. A pipeline can be created, and only the rows needed are pulled through each stage.

This has served Nushell well. It's a simple, yet powerful, abstraction that commands can build on top of.

That said, I think it's time to experiment with a more powerful model. If this proves successful, it could put Nushell in a much better position for both higher performance but also a much more improved user experience.

Pipelines are too simple

View builder.rs
let x = {
let mut foo = Foo::new();
foo.append("bar");
foo.append("baz");
x
};
// or, more commonly:
View rabbithole.rs
extern crate syn;
use std::env;
use std::fs;
use std::io::Read;
#[derive(Debug)]
struct File {
functions: Vec<Function>,
}
View gui_in_rust.md

GUI support in Rust could be built on a central crate that has a common set of GUI components (button, menubar, dropdown, etc). It would also have first-party connectors to the GUI that's "native" for a given platform.

Wrapping native GUI by default means that each app will feel natural for the given platform, including not only the look of the widgets, but also tricky elements like text input would work just as they with native inputs. The native platforms already put a ton of work into getting this right, let's just leverage it.

Because of the difference in platforms, what if the GUI crate didn't take (x,y) coordinates, but instead used a more constraint-based layout mechanism. This may help cut down on the fiddling you have to do when getting it to work well between platforms.

Rust has a very "fluent" style, and it's not uncommon to see people use this to simulate optional named arguments. I think we could use this to give the library a bit of a Rust feel.

Simple example:

View emacs_rls.md
View conflicting_implementation.rs
trait FnRegister<A, RetVal, Args> {
fn register(&mut self, name: &str, f: A);
}
impl<A, RetVal, Arg1> FnRegister<A, RetVal, (Arg1,)> for Bob
where A: 'static + Fn(Arg1)->RetVal
{
fn register(self, name: &str, fun: fn(Arg1)->RetVal) {
println!("Fn(T)->U");
}
View register_fn_constraints3.rs
use std::any::Any;
#[derive(Debug)]
struct Engine {
x: i32
}
trait FnRegister<RetVal, Args> {
fn register<T: Fn(Args)->RetVal>(&mut self, name: &str, f: T);
}
View register_fn_constraints2.rs
use std::any::Any;
#[derive(Debug)]
struct Engine {
x: i32
}
trait FnRegister1V {
fn register<T: Fn(U)->V, U, V>(&mut self, name: &str, f: T);
}
View register_fn_constraints.rs
use std::any::Any;
#[derive(Debug)]
struct Engine {
x: i32
}
trait FnRegisterTest {
fn register(self, engine: &mut Engine, name: &str);
}
View register_fn.md

Try 1: Constrain with FnRegister

pub fn register<T: FnRegister>(&mut self, f: T, name: &str) {
    f.register(self, name);
}

Error: the trait rhai::fn_register::FnRegister is not implemented for the type fn(i32) -> i32 {add} [E0277]