Skip to content

Commit db85ed0

Browse files
committedAug 19, 2019
First commit
1 parent 2229f62 commit db85ed0

File tree

3 files changed

+233
-0
lines changed

3 files changed

+233
-0
lines changed
 

‎.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
/target
2+
**/*.rs.bk
3+
Cargo.lock

‎Cargo.toml

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[package]
2+
name = "advancedresearch-higher_order_core"
3+
version = "0.1.0"
4+
authors = ["Sven Nilsen <bvssvni@gmail.com>"]
5+
edition = "2018"
6+
7+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
8+
9+
[lib]
10+
name = "higher_order_core"
11+
12+
[dependencies]

‎src/lib.rs

+218
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
#![deny(missing_docs)]
2+
3+
//! # Higher Order Core
4+
//!
5+
//! This crate contains core structs and traits for programming with higher order data structures.
6+
//!
7+
//! ### Introduction to higher order data structures
8+
//!
9+
//! A higher order data structure is a generalization of an ordinary data structure.
10+
//!
11+
//! In ordinary data structures, the default way of programming is:
12+
//!
13+
//! - Use data structures for data
14+
//! - Use methods/functions for operations on data
15+
//!
16+
//! In a higher order data structure, data and functions become the same thing.
17+
//!
18+
//! The central idea of an higher order data structure,
19+
//! is that properties can be functions of the same type.
20+
//!
21+
//! For example, a `Point` has an `x`, `y` and `z` property.
22+
//! In ordinary programming, `x`, `y` and `z` might have the type `f64`.
23+
//!
24+
//! If `x`, `y` and `z` are functions from `T -> f64`,
25+
//! then the point type is `Point<T>`.
26+
//!
27+
//! A higher order `Point<T>` can be called, just like a function.
28+
//! When called as a function, `Point<T>` returns `Point`.
29+
//!
30+
//! However, unlike functions, you can still access properties of `Point<T>`.
31+
//! You can also define methods and overload operators for `Point<T>`.
32+
//! This means that in a higher order data structure, data and functions become the same thing.
33+
//!
34+
//! ### Motivation of programming with higher order data structures
35+
//!
36+
//! The major application of higher order data structures is geometry.
37+
//!
38+
//! An typical usage is e.g. to create procedurally generated content for games.
39+
//!
40+
//! Higher order data structures is about finding the right balance between
41+
//! hiding implementation details and exposing them for various generic algorithms.
42+
//!
43+
//! For example, a unit circle can be thought of as having the type `Point<f64>`.
44+
//! The argument can be an angle in radians, or a value in the unit interval `[0, 1]`.
45+
//!
46+
//! Another example, a line can be thought of as having the type `Point<f64>`.
47+
//! The argument is a value in the unit interval `[0, 1]`.
48+
//! When called with `0`, you get the start point of the line.
49+
//! When called with `1`, you get the end point of the line.
50+
//!
51+
//! Instead of declaring a `Circle` type, a `Line` type and so on,
52+
//! one can use `Point<f64>` to represent both of them.
53+
//!
54+
//! Higher order data structures makes easier to write generic algorithms for geometry.
55+
//! Although it seems abstract at first, it is also practically useful in unexpected cases.
56+
//!
57+
//! For example, an animated point can be thought of as having the type `Point<(&[Frame], f64)>`.
58+
//! The first argument contains the animation data and the second argument is time in seconds.
59+
//! Properties `x`, `y` and `z` of an animated point determines how the animated time is computed.
60+
//! The details of the implementation can be hidden from the algorithm that uses animated points.
61+
//!
62+
//! Sometimes you need to work with complex geometry.
63+
//! In these cases, it is easier to work with higher order data structures.
64+
//!
65+
//! For example, a planet might have a center, equator, poles, surface etc.
66+
//! A planet orbits around a star, which orbits around the center of a galaxy.
67+
//! This means that the properties of a planet, viewed from different reference frames,
68+
//! are functions of the arguments that determine the reference frame.
69+
//! You can create a "higher order planet" to reason about a planet's properties
70+
//! under various reference frames.
71+
//!
72+
//! ### Design
73+
//!
74+
//! Here is an example of how to declare a new higher order data structure:
75+
//!
76+
//! ```rust
77+
//! use higher_order_core::{Ho, Call, Arg, Func};
78+
//! use std::sync::Arc;
79+
//!
80+
//! /// Higher order 3D point.
81+
//! #[derive(Clone)]
82+
//! pub struct Point<T = ()> where f64: Ho<T> {
83+
//! /// Function for x-coordinates.
84+
//! pub x: <f64 as Ho<T>>::Fun,
85+
//! /// Function for y-coordinates.
86+
//! pub y: <f64 as Ho<T>>::Fun,
87+
//! /// Function for z-coordinates.
88+
//! pub z: <f64 as Ho<T>>::Fun,
89+
//! }
90+
//!
91+
//! // It is common to declare a type alias for functions, e.g:
92+
//! pub type PointFunc<T> = Point<Arg<T>>;
93+
//!
94+
//! // Implement `Ho<Arg<T>>` to allow higher order data structures
95+
//! // using properties `<Point as Ho<T>>::Fun`.
96+
//! impl<T: Clone> Ho<Arg<T>> for Point {
97+
//! type Fun = PointFunc<T>;
98+
//! }
99+
//!
100+
//! // Implement `Call<T>` to allow higher order calls.
101+
//! impl<T: Copy> Call<T> for Point
102+
//! where f64: Ho<Arg<T>> + Call<T>
103+
//! {
104+
//! fn call(f: &Self::Fun, val: T) -> Point {
105+
//! Point::<()> {
106+
//! x: <f64 as Call<T>>::call(&f.x, val),
107+
//! y: <f64 as Call<T>>::call(&f.y, val),
108+
//! z: <f64 as Call<T>>::call(&f.z, val),
109+
//! }
110+
//! }
111+
//! }
112+
//!
113+
//! impl<T> PointFunc<T> {
114+
//! /// Helper method for calling value.
115+
//! pub fn call(&self, val: T) -> Point where T: Copy {
116+
//! <Point as Call<T>>::call(self, val)
117+
//! }
118+
//! }
119+
//!
120+
//! // Operations are usually defined as simple traits.
121+
//! // They look exactly the same as for normal generic programming.
122+
//! /// Dot operator.
123+
//! pub trait Dot<Rhs = Self> {
124+
//! /// The output type.
125+
//! type Output;
126+
//!
127+
//! /// Returns the dot product.
128+
//! fn dot(self, other: Rhs) -> Self::Output;
129+
//! }
130+
//!
131+
//! // Implement operator once for the ordinary case.
132+
//! impl Dot for Point {
133+
//! type Output = f64;
134+
//! fn dot(self, other: Self) -> f64 {
135+
//! self.x * other.x +
136+
//! self.y * other.y +
137+
//! self.z * other.z
138+
//! }
139+
//! }
140+
//!
141+
//! // Implement operator once for the higher order case.
142+
//! impl<T: 'static + Copy> Dot for PointFunc<T> {
143+
//! type Output = Func<T, f64>;
144+
//! fn dot(self, other: Self) -> Func<T, f64> {
145+
//! let ax = self.x;
146+
//! let ay = self.y;
147+
//! let az = self.z;
148+
//! let bx = other.x;
149+
//! let by = other.y;
150+
//! let bz = other.z;
151+
//! Arc::new(move |a| ax(a) * bx(a) + ay(a) * by(a) + az(a) * bz(a))
152+
//! }
153+
//! }
154+
//! ```
155+
//!
156+
//! To disambiguate impls of e.g. `Point<()>` from `Point<T>`,
157+
//! an argument type `Arg<T>` is used for point functions: `Point<Arg<T>>`.
158+
//!
159+
//! For every higher order type `U` and and argument type `T`,
160+
//! there is an associated function type `T -> U`.
161+
//!
162+
//! For primitive types, e.g. `f64`, the function type is `Func<T, f64>`.
163+
//!
164+
//! For higher order structs, e.g. `X<()>`, the function type is `X<Arg<T>>`.
165+
//!
166+
//! The code for operators on higher order data structures must be written twice:
167+
//!
168+
//! - Once for the ordinary case `X<()>`
169+
//! - Once for the higher order case `X<Arg<T>>`
170+
171+
use std::sync::Arc;
172+
173+
/// Standard function type.
174+
pub type Func<T, U> = Arc<dyn Fn(T) -> U + Send + Sync>;
175+
176+
/// Used to disambiguate impls for Rust's type checker.
177+
#[derive(Copy, Clone)]
178+
pub struct Arg<T>(pub T);
179+
180+
/// Implemented by higher order types.
181+
///
182+
/// A higher order type might be a concrete value,
183+
/// or it might be a function of some input type `T`.
184+
///
185+
/// Each higher order type has an associated function type
186+
/// for any argument of type `T`.
187+
///
188+
/// This makes it possible to e.g. associate `PointFunc<T>` with `Point`.
189+
pub trait Ho<T>: Sized {
190+
/// The function type.
191+
type Fun: Clone;
192+
}
193+
194+
/// Implemented by higher order calls.
195+
pub trait Call<T>: Ho<Arg<T>> {
196+
/// Calls function with some value.
197+
fn call(f: &Self::Fun, val: T) -> Self;
198+
}
199+
200+
impl<T, U> Call<T> for U
201+
where U: Ho<Arg<T>, Fun = Func<T, Self>> {
202+
fn call(f: &Self::Fun, val: T) -> Self {f(val)}
203+
}
204+
205+
impl<T: Clone> Ho<()> for T {type Fun = T;}
206+
207+
impl<T> Ho<Arg<T>> for f64 {type Fun = Func<T, f64>;}
208+
impl<T> Ho<Arg<T>> for f32 {type Fun = Func<T, f32>;}
209+
impl<T> Ho<Arg<T>> for u8 {type Fun = Func<T, u8>;}
210+
impl<T> Ho<Arg<T>> for u16 {type Fun = Func<T, u16>;}
211+
impl<T> Ho<Arg<T>> for u32 {type Fun = Func<T, u32>;}
212+
impl<T> Ho<Arg<T>> for u64 {type Fun = Func<T, u64>;}
213+
impl<T> Ho<Arg<T>> for usize {type Fun = Func<T, usize>;}
214+
impl<T> Ho<Arg<T>> for i8 {type Fun = Func<T, i8>;}
215+
impl<T> Ho<Arg<T>> for i16 {type Fun = Func<T, i16>;}
216+
impl<T> Ho<Arg<T>> for i32 {type Fun = Func<T, i32>;}
217+
impl<T> Ho<Arg<T>> for i64 {type Fun = Func<T, i64>;}
218+
impl<T> Ho<Arg<T>> for isize {type Fun = Func<T, isize>;}

0 commit comments

Comments
 (0)
Please sign in to comment.