Skip to content

Instantly share code, notes, and snippets.

@charleso
Last active October 10, 2018 22:22
Show Gist options
  • Save charleso/a8bfd79ee4420980043014122d5b11fe to your computer and use it in GitHub Desktop.
Save charleso/a8bfd79ee4420980043014122d5b11fe to your computer and use it in GitHub Desktop.
Generic functional apply using Reader - replying to https://twitter.com/typesthings/status/1049983980395610113
-- A data type with a generic result for each function
data Foo m =
Foo {
a :: Int -> m Bah
, b :: String -> String -> m Bah
}
-- Generic helper for applying a function to each function of Foo
--
-- Depending on the intent of the question this might be "cheating" because
-- we're "manually" calling "f" on every function of Foo.
-- It's important to note though that it has nothing to do with "Reader", it's completely generic.
-- We could _possibly_ even generate this function:
-- https://github.com/Gabriel439/Haskell-MMorph-Library/issues/3
hoistFoo :: (forall a. m a -> n a) -> Foo m -> Foo n
hoistFoo f foo =
Foo (\x1 -> f (a foo x1)) (\x1 x2 -> f (b foo x1 x2))
/** Provided by Java 8 */
public interface Function<A, B> {
public B apply(A a);
}
public interface Foo<A> {
public Function<A, Bah> a(Int y);
public Function<A, Bah> b(String s, String t);
/* The hoist function */
default <B> Foo<B> map(Function<A, B> f) {
return new Foo<> {
public Function<B, Bah> a(Int y) {
return b -> this.a(y).apply(b)
}
public Function<B, Bah> b(String s, String t) {
return b -> this.b(s, t).apply(b)
}
}
}
}
public class Main {
public static void main() {
Foo<Int> foo = new Foo {
public Function<Int, Bah> a(Int y) {
return b -> ...
}
public Function<Int, Bah> b(String s, String t) {
return b -> ...
}
};
// Ignore the argument that's passed in and return 1
Foo<Void> foo2 = foo.<Int, Void>map(void -> 1);
// This might not compile, not sure if null is a void, but hopefully you get the idea.
foo2.b("s", "t").apply(null);
}
}
-- Note the type signature, the generic part of each function
-- is itself _another_ function that takes an Int
foo :: Foo ((->) Int)
foo =
Foo {
a = \y x -> ...
, b = \s t x -> ...
}
main :: Bah
main =
let
-- () is "unit" which is an empty value.
-- So we've replaced our generic function that takes an Int with one that takes unit.
-- There might be a slightly cleaner way to do this, this was done in a rush
foo2 :: Foo ((->) ())
-- Apply "1" to every function in our object
foo2 = hoistFoo (\x () -> x 1) foo
in
-- Call the "b" function on the partially applied foo
-- Note the () which is needed as per above comment
b foo2 "s" "t" ()
-- This example is _basically_ the same as the one above, but using the tranformers library
-- which is very common in Haskell programs because it's so useful
-- http://hackage.haskell.org/package/transformers
import Control.Monad.Trans.Reader
-- For this _specific_ instance of Foo every function has an extra int argument
foo :: Foo (Reader Int)
foo =
Foo {
a = \y -> Reader $ \x -> ...
, b = \s t -> Reader $ \x -> ...
}
main :: Bah
main =
let
-- Identity just represents "a value", we've applied everything to our Reader at this point
foo2 :: Foo Identity
-- Apply "1" to every function in our object
foo2 = hoistFoo (runReaderT 1) foo
in
-- Get the value out of Identity
runIdentity $
-- Call the "b" function on the partially applied foo
b foo2 "s" "t"
data Foo x =
Foo {
a :: x -> Int -> Bah
, b :: x -> String -> String -> Bah
}
-- NOTE: The order of x and y are reversed here
hoistFoo :: (y -> x) -> Foo x -> Foo y
hoistFoo f foo =
Foo (\x1 x2 -> a foo (f x1) x2) (\x1 x2 x3 -> b foo (f x1) x2 x2)
main :: Bah
main =
let
foo2 :: Foo ()
foo2 = hoistFoo (\() -> 1) foo
in
b foo2 () "s" "t"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment