Created
March 26, 2016 10:44
-
-
Save sharpjs/31f83fa4f2e258bcd72a to your computer and use it in GitHub Desktop.
Functor (fmap) for Rust
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Functors (fmap) for Rust | |
// | |
// Copyright (C) 2016 Jeffrey Sharp | |
// | |
// This file is free software: you can redistribute it and/or modify it | |
// under the terms of the GNU General Public License as published | |
// by the Free Software Foundation, either version 3 of the License, | |
// or (at your option) any later version. | |
// | |
// This file is distributed in the hope that it will be useful, but | |
// WITHOUT ANY WARRANTY; without even the implied warranty of | |
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See | |
// the GNU General Public License for more details. | |
// | |
// You should have received a copy of the GNU General Public License | |
// along with this file. If not, see <http://www.gnu.org/licenses/>. | |
pub trait Fmap<T, U> { | |
type Out; | |
fn fmap<F: Fn(&T) -> U>(&self, F) -> Self::Out; | |
} | |
// LESSON LEARNED: Rust does not (yet) have higher-kinded types (HKT), | |
// which are necessary to express Functor like it is in Haskell. | |
// For now, we have to provide both the T and U types. | |
// | |
// Rust also does not (yet) have variadic generic tuples. So, we have | |
// to implement Fmap for every size of tuple that we need. | |
#[derive(Clone, Copy, Eq, PartialEq, Hash, Debug)] | |
pub struct Ctx<T, C> (pub T, pub C); | |
// Arity 0 | |
impl<T, U> Fmap<T, U> for () { | |
type Out = (); | |
#[inline(always)] | |
fn fmap<F: Fn(&T) -> U>(&self, f: F) -> Self::Out { | |
() | |
} | |
} | |
// Arity 1 | |
impl<T, U> Fmap<T, U> for (T,) { | |
type Out = (U,); | |
#[inline(always)] | |
fn fmap<F: Fn(&T) -> U>(&self, f: F) -> Self::Out { | |
(f(&self.0),) | |
} | |
} | |
// Arity 2 | |
impl<T, U> Fmap<T, U> for (T, T) { | |
type Out = (U, U); | |
#[inline] | |
fn fmap<F: Fn(&T) -> U>(&self, f: F) -> Self::Out { | |
(f(&self.0), f(&self.1)) | |
} | |
} | |
// Arity 3 | |
impl<T, U> Fmap<T, U> for (T, T, T) { | |
type Out = (U, U, U); | |
#[inline] | |
fn fmap<F: Fn(&T) -> U>(&self, f: F) -> Self::Out { | |
(f(&self.0), f(&self.1), f(&self.2)) | |
} | |
} | |
// Arity 4 | |
impl<T, U> Fmap<T, U> for (T, T, T, T) { | |
type Out = (U, U, U, U); | |
#[inline] | |
fn fmap<F: Fn(&T) -> U>(&self, f: F) -> Self::Out { | |
(f(&self.0), f(&self.1), f(&self.2), f(&self.3)) | |
} | |
} | |
// Any arity with context | |
impl<T, U, M, C> Fmap<T, U> for Ctx<M, C> | |
where M: Fmap<T, U>, | |
C: Copy { | |
type Out = Ctx<M::Out, C>; | |
#[inline] | |
fn fmap<F: Fn(&T) -> U>(&self, f: F) -> Self::Out { | |
let &Ctx(ref m, c) = self; | |
Ctx(m.fmap(f), c) | |
} | |
} | |
#[cfg(test)] | |
mod tests { | |
use super::*; | |
#[test] | |
fn fmap_0() { | |
assert_eq!( (), ().fmap(negate) ); | |
} | |
#[test] | |
fn fmap_1() { | |
assert_eq!( (-1,), (1,).fmap(negate) ); | |
} | |
#[test] | |
fn fmap_2() { | |
assert_eq!( (-1, -2), (1, 2).fmap(negate) ); | |
} | |
#[test] | |
fn fmap_3() { | |
assert_eq!( (-1, -2, -3), (1, 2, 3).fmap(negate) ); | |
} | |
#[test] | |
fn fmap_4() { | |
assert_eq!( (-1, -2, -3, -4), (1, 2, 3, 4).fmap(negate) ); | |
} | |
#[test] | |
fn fmap_ctx() { | |
assert_eq!( Ctx((-1, -2), "a"), Ctx((1, 2), "a").fmap(negate) ); | |
} | |
fn negate(x: &u8) -> i16 { -(*x as i16) } | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment