Last active
October 10, 2018 22:22
-
-
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
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
-- 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)) |
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
/** 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); | |
} | |
} |
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
-- 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 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
-- 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" |
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
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