Skip to content

Commit

Permalink
evolve type constructors
Browse files Browse the repository at this point in the history
  • Loading branch information
Wandalen committed May 25, 2022
1 parent 4dd2b3a commit fa7be66
Show file tree
Hide file tree
Showing 26 changed files with 505 additions and 185 deletions.
43 changes: 43 additions & 0 deletions doc/rust/WhatStdIsMissing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# What is missing in STD of Rust

Rust provides very profound standard library. However it lacks few things.

## Iteration pattern

Iterator is a behavioral design pattern that lets you traverse elements of a collection without exposing its underlying representation.
Rust supports it. Collections expose methods [iter()](https://doc.rust-lang.org/1.56.1/std/collections/struct.HashMap.html#method.iter) and [iter_mut()](https://doc.rust-lang.org/1.56.1/std/collections/struct.HashMap.html#method.iter_mut) to iterate its elements. Method `iter()` returns something implementing abstract interface of trat [Iterator](https://doc.rust-lang.org/1.56.1/std/iter/trait.Iterator.html). Beauty of that pattern is sepeartion of data and algorithm. Hundreds of algorithms are implemented for based on the simple interface, indeed in Rust it has only one method [next()](https://doc.rust-lang.org/1.56.1/std/iter/trait.Iterator.html#tymethod.next). Having that method implemented for any particualr data type make possible to use any algorithm which is based on the interface.

All collections implement interface of trait [IntoIterator](https://doc.rust-lang.org/1.56.1/core/iter/trait.IntoIterator.html) with help of which it's possible to convert the collection into a iterator giving up elements of it. Structures which implements interface of trait [Extend](https://doc.rust-lang.org/1.56.1/core/iter/trait.Extend.html) can consume another collection adopting its elements. Traits [ExactSizeIterator](https://doc.rust-lang.org/1.56.1/core/iter/trait.ExactSizeIterator.html) and [FusedIterator](https://doc.rust-lang.org/1.56.1/core/iter/trait.FusedIterator.html) give additional promises rectricting implementor of these interface. [Peekable](https://doc.rust-lang.org/1.56.1/std/iter/struct.Peekable.html) makes possible to iterate speculatively, looking forward the next element without advancing. Use function [repeat](https://doc.rust-lang.org/1.56.1/std/iter/fn.repeat.html) to construct an iterator which repeat the same element endlessly. Use [zip](https://doc.rust-lang.org/1.56.1/std/iter/fn.zip.html) if you want to pair elements of two collections with the same length. But there is the catch. Currently [zip](https://doc.rust-lang.org/1.56.1/std/iter/fn.zip.html) is experimental and it is not available in stable Rust.

All that is good. But what if you need to peek more than one element forward? What if you need to repeat an element finite number of times?
At this moment you cant do anything of that easily with Rust STD library.
To speculatively peek more than one element use [multipeek](https://docs.rs/itertools/0.10.3/itertools/fn.multipeek.html). To repeat the same element `n` times use [repeat_n](https://docs.rs/itertools/0.10.3/itertools/fn.repeat_n.html). To zip more than two collection use [multizip](https://docs.rs/itertools/0.10.3/itertools/fn.multizip.html). More over all this functionality available in stable Rust! There are many more algorithm whih are based on the interface, but is not part of stable Rust STD library in [crate itertools](https://docs.rs/itertools/0.10.3/itertools/index.html).

So why such useful algorithms is not part of STD?
<!-- xxx : answer please -->
Does lack of advance algorithms and data types has positive effect on time of compilation and size of compiled executable?
I don't know. <!-- xxx : answer please -->

<!-- - literally -->
<!-- - drive more -->
<!-- - prelude does not include hashmap and other containers, but include ... -->
<!-- - std does not implement variadic constructor of hashmap, but vector ... -->
<!-- - variadic make -->
<!-- - type constructors -->

## Derive more

Fundamental problem of a programming language is ability to write programs on the same dialect. C++ has 3 way to express algorithms:
- statically with help of macroses
- statically with help of templates
- dynamically with help of ordinary C++
Disadvantage is developer should often has his algorithm for 3 different domains, express each in 3 different dialects and it's not possible to have it in one dialect.
That is the biggest advantage of such language as JS nad Python over C++ and Rust, developer don't need to have 3 copies of the same algorithm.

In Rust situation is better than in C++. [Declarative macroses](https://www.youtube.com/watch?v=q6paRBbLgNw) has its niche, but for generating code usually used [procedural macroses](https://www.youtube.com/watch?v=geovSK3wMB8). Procedural macroses are written in pure Rust. Although in Rust stage of compilation has explicit boundary and developer have to write programs which generates programs. It's called metaprogramming.

In JS there is no boundary between stage of generation of a program and program itself and metapgram on JS looks exactly the same as ordinary program. In Rust there is several means to container a metaprogram. [Derive](https://doc.rust-lang.org/stable/reference/attributes/derive.html#derive) is the primaraly mean to do that. Derive is written on Rust and it generates Rust code from Rust code on input.

Rust have many derives shiped with Rust in STD library. Some of them are


20 changes: 20 additions & 0 deletions module/rust/type_constructor/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,26 @@ path = "sample/rust/type_constructor_parametrized_element_sample/src/main.rs"
name = "type_constructor_parametrized_tuple_sample"
path = "sample/rust/type_constructor_parametrized_tuple_sample/src/main.rs"

[[example]]
name = "type_constructor_multiple_sample"
path = "sample/rust/type_constructor_multiple_sample/src/main.rs"

[[example]]
name = "type_constructor_without_macro_sample"
path = "sample/rust/type_constructor_without_macro_sample/src/main.rs"

[[example]]
name = "type_constructor_pair_sample"
path = "sample/rust/type_constructor_pair_sample/src/main.rs"

[[example]]
name = "type_constructor_homopair_sample"
path = "sample/rust/type_constructor_homopair_sample/src/main.rs"

[[example]]
name = "type_constructor_many_sample"
path = "sample/rust/type_constructor_many_sample/src/main.rs"

[dependencies]

[dev-dependencies]
Expand Down
185 changes: 175 additions & 10 deletions module/rust/type_constructor/Readme.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,77 @@
# Module :: type_constructor
[![experimental](https://img.shields.io/badge/stability-experimental-orange.svg)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/ModuleFundamentalDataTypePush.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/ModuleFundamentalDataTypePush.yml) [![docs.rs](https://img.shields.io/docsrs/type_constructor?color=e3e8f0&logo=docs.rs)](https://docs.rs/type_constructor) [![discord](https://img.shields.io/discord/872391416519737405?color=e3e8f0&logo=discord&logoColor=e3e8f0)](https://discord.gg/JwTG6d2b)

Fundamental data types and type constructors, like Single, Pair, Many.
Fundamental data types and type constructors, like Single, Pair, Homopair, Many.

- `Single` to wrap single element.
- `Pair` to wrap pair of distinct elements.
- `HomoPair` to wrap pair of elements with the same type.
- `Many` to wrap `Vec` of elements.

## Macro `types` for type constructing

The same macro `types` is responsible for generating code for Single, Pair, Homopair, Many. Each type constructor has its own keyword for that, but Pair and Homopair use the same keyword difference is number of constituent types. It is possible to define all types at once.

```rust
use type_constructor::prelude::*;

types!
{

single MySingle : f32;
single SingleWithParametrized : std::sync::Arc< T : Copy >;
single SingleWithParameter : < T >;

pair MyPair : f32;
pair PairWithParametrized : std::sync::Arc< T1 : Copy >, std::sync::Arc< T2 : Copy >;
pair PairWithParameter : < T1, T2 >;

pair MyHomoPair : f32;
pair HomoPairWithParametrized : std::sync::Arc< T : Copy >;
pair HomoPairWithParameter : < T >;

many MyMany : f32;
many ManyWithParametrized : std::sync::Arc< T : Copy >;
many ManyWithParameter : < T >;

}
```

It generates more than 1000 lines of code, which otherwise you would have to write manually.

## Without macro

Macro `types` is exposed to generate new types, but in some cases it is enough to reuse already generated types of such kind. The library ships such types: Single, Pair, Homopair, Many. Note: avoiding generating of new types you will get in position to been not able to define your own implementation of foraign traits.

```rust
let i32_in_tuple = type_constructor::Single::< i32 >::from( 13 );
dbg!( i32_in_tuple );
// i32_in_tuple = Single( 13 )
let i32_and_f32_in_tuple = type_constructor::Pair::< i32, f32 >::from( ( 13, 13.0 ) );
dbg!( i32_and_f32_in_tuple );
// vec_of_i32_in_tuple = Pair( 13, 13.0 )
let two_i32_in_tuple = type_constructor::HomoPair::< i32 >::from( ( 13, 31 ) );
dbg!( two_i32_in_tuple );
// vec_of_i32_in_tuple = HomoPair( 13, 31 )
let vec_of_i32_in_tuple = type_constructor::Many::< i32 >::from( [ 1, 2, 3 ] );
dbg!( vec_of_i32_in_tuple );
// vec_of_i32_in_tuple = Many([ 1, 2, 3 ])
```

## Single

Type constructor to define tuple wrapping a given type.

Quite often you need to wrap a given type into new one.
For example if orphan rule became and obstacle one should introduce a new type wrapping foreing one.
Type constructr `types!` does exaclty that and auto-implement traits From, Into and Deref for the constructed type.
Type constructr does exaclty that and auto-implement traits From, Into and Deref for the constructed type.

## Sample :: homopair with parameters

Unlike `heteropair` `homopair` has much more traits implemented for it. Among such are: `clone_as_tuple`, `clone_as_array` to clone it as either tuple or arrat, `as_tuple`, `as_array`, `as_slice` to reinterpret it as either tuple or array or slice, traits `From`/`Into` are implemented to convert it from/into tupe, array, slice, scalar.

### Make.

## Make.

Make is variadic constructor. It's unified interface of arbitrary-length constructor.
After implementing several traits `Make0`, `Make1` up to `MakeN` one can use macrk `make!` to construct instances.
Expand Down Expand Up @@ -67,7 +127,7 @@ let x = MySingle( 13 );
println!( "x : {}", x.0 );
```

### Sample :: single with derives and attributes.
## Sample :: single with derives and attributes.

It's possible to define attributes as well as derives.

Expand Down Expand Up @@ -119,7 +179,7 @@ let x = MySingle( 13 );
dbg!( x );
```

### Sample :: single with struct instead of macro.
## Sample :: single with struct instead of macro.

Sometimes it's sufficient to use common type instead of defining a brand new.
You may use paramtetrized struct `Single< T >` instead of macro `types!` if that is the case.
Expand All @@ -130,7 +190,7 @@ let x = Single::< i32 >( 13 );
dbg!( x );
```

### Sample :: single with parametrized element.
## Sample :: single with parametrized element.

Element of tuple could be parametrized.

Expand Down Expand Up @@ -178,7 +238,7 @@ impl< T : Copy > From< MySingle< T > > for std::sync::Arc< T >
let x = MySingle( std::sync::Arc::new( 13 ) );
```

### Sample :: single with parametrized tuple.
## Sample :: single with parametrized tuple.

Instead of parametrizing the element it's possible to define a parametrized tuple.

Expand Down Expand Up @@ -223,7 +283,112 @@ let x = MySingle( 13 );
dbg!( 13 );
```

### Sample :: make - variadic constructor
## Sample :: single-line pair

Sometimes you need to wrap more than single element into a tupe. If types of elements are different use `pair`. The same macro `types` is responsible for generating code for both `single`, `pair` and also `many`.

```rust
use type_constructor::prelude::*;

types!( pair MyPair : i32, i64 );
let x = MyPair( 13, 31 );
println!( "x : ( {}, {} )", x.0, x.1 );
// prints : x : ( 13, 31 )
```

It gererates code:

```rust
```

## Sample :: pair with parameters

Just like `single` `pair` may have parameters.

```rust
use type_constructor::prelude::*;

use core::fmt;
types!
{
#[ derive( Debug ) ]
pair MyPair : < T1 : fmt::Debug, T2 : fmt::Debug >;
}
let x = MyPair( 13, 13.0 );
dbg!( x );
// prints : x = MyPair( 13, 13.0 )
```

It gererates code:

```rust
```

## Sample :: single-line homopair

If you need to wrap pair of elements with the same type use type constructor `pair`. The same type constructor `pair` for both `pair` and `homopair`, difference in number of types in definition, `homopair` has only one, because both its element has the same type. The same macro `types` is responsible for generating code for both `single`, `pair` and also `many`.

```rust
use type_constructor::prelude::*;

types!( pair MyPair : i32, i64 );
let x = MyPair( 13, 31 );
println!( "x : ( {}, {} )", x.0, x.1 );
// prints : x : ( 13, 31 )
```

It gererates code:

```rust
```

## Sample :: homopair with parameters

Unlike `heteropair` `homopair` has much more traits implemented for it. Among such are: `clone_as_tuple`, `clone_as_array` to clone it as either tuple or arrat, `as_tuple`, `as_array`, `as_slice` to reinterpret it as either tuple or array or slice, traits `From`/`Into` are implemented to convert it from/into tupe, array, slice, scalar.

```rust
use type_constructor::prelude::*;

use core::fmt;
types!
{
#[ derive( Debug ) ]
pair MyHomoPair : < T : fmt::Debug >;
}
let x = MyHomoPair( 13, 31 );
dbg!( &x );
// prints : &x = MyHomoPair( 13, 31 )
let clone_as_array : [ i32 ; 2 ] = x.clone_as_array();
dbg!( &clone_as_array );
// prints : &clone_as_array = [ 13, 31 ]
let clone_as_tuple : ( i32 , i32 ) = x.clone_as_tuple();
dbg!( &clone_as_tuple );
// prints : &clone_as_tuple = ( 13, 31 )
```

It gererates code:

```rust
```

## Sample :: single-line many

Use type constructor `many` to wrap `Vec` in a tuple. Similar to `single` it has essential traits implemented for it.

```rust
use type_constructor::prelude::*;

types!( many MyMany : i32 );
let x = MyMany::from( [ 1, 2, 3 ] );
println!( "x : {:?}", x.0 );
```

It gererates code:

```rust
```

## Sample :: make - variadic constructor

Implement traits [Make0], [Make1] up to MakeN to provide interface to construct your structure with different set of arguments.
In this example structure Struct1 could be constructed either without arguments, with single argument or with two arguments.
Expand Down Expand Up @@ -278,13 +443,13 @@ let exp = Struct1{ a : 1, b : 3 };
assert_eq!( got, exp );
```

### To add to your project
## To add to your project

``` shell
cargo add type_constructor
```

### Try out from the repository
## Try out from the repository

``` shell test
git clone https://github.com/Wandalen/wTools
Expand Down
4 changes: 3 additions & 1 deletion rust/impl/automata/wautomata_lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,6 @@ pub mod prelude
// - node get closed neighbourhood?
// - node get degree ( nodes )
// - node get size ( edges )
//
//
// - Hamilton circuit?
// -
1 change: 0 additions & 1 deletion rust/impl/dt/type_constructor/make.rs
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,5 @@ pub mod prelude

make,


};
}
8 changes: 4 additions & 4 deletions rust/impl/dt/type_constructor/many.rs
Original file line number Diff line number Diff line change
Expand Up @@ -243,15 +243,15 @@ mod internal
}

impl
$( < $( $ParamName $( : $ParamTy1x1 $( :: $ParamTy1xN )* $( + $ParamTy2 )* )? ),* > )?
< $( $( $ParamName $( : $ParamTy1x1 $( :: $ParamTy1xN )* $( + $ParamTy2 )* )? , )* )? const N : usize >
From
< [ $TypeSplit1 $( :: $TypeSplitN )* $( < $( $ParamName ),* > )? ; 1 ] >
< [ $TypeSplit1 $( :: $TypeSplitN )* $( < $( $ParamName ),* > )? ; N ] >
for $Name
$( < $( $ParamName ),* > )?
where
$TypeSplit1 $( :: $TypeSplitN )* $( < $( $ParamName ),* > )? : Clone,
{
fn from( src : [ $TypeSplit1 $( :: $TypeSplitN )* $( < $( $ParamName ),* > )? ; 1 ] ) -> Self
fn from( src : [ $TypeSplit1 $( :: $TypeSplitN )* $( < $( $ParamName ),* > )? ; N ] ) -> Self
{
Self( std::vec::Vec::from( src ) )
}
Expand All @@ -268,7 +268,7 @@ mod internal
{
fn from( src : &[ $TypeSplit1 $( :: $TypeSplitN )* $( < $( $ParamName ),* > )? ] ) -> Self
{
debug_assert_eq!( src.len(), 1 );
debug_assert_eq!( src.len(), 1 ); // xxx : add test with long slice
Self( std::vec::Vec::from( src ) )
}
}
Expand Down
5 changes: 2 additions & 3 deletions rust/impl/dt/type_constructor/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,10 @@ mod internal
{
use crate::exposed::*;

// xxx : register graph_tools
// xxx : no std
// xxx : samples
// qqq : paste generated code for each sample
// qqq : for Dima : paste generated code for each sample
//
// xxx : register std_tools and std_x
// xxx : redo implements
// xxx : add core::fmt to prelude
// xxx : write article about the module
Expand Down
Loading

0 comments on commit fa7be66

Please sign in to comment.