Created
May 6, 2021 21:40
-
-
Save hadronized/e270747f88c4461ef0d9f8bdae1c7824 to your computer and use it in GitHub Desktop.
This file contains 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
{-# LANGUAGE ConstraintKinds #-} | |
{-# LANGUAGE GADTs #-} | |
{-# LANGUAGE KindSignatures #-} | |
{-# LANGUAGE Rank2Types #-} | |
import Data.Kind (Constraint) | |
-- This implementation is based on existentials; i.e. the type information is | |
-- lost when the type 'a' is wrapped in 'Impl c' (no 'a' anymore). | |
data Impl :: (* -> Constraint) -> * where | |
Impl :: forall a c. c a => a -> Impl c | |
-- Inspect the content of the existential, which is the Haskell safe way to | |
-- unwrap the existential and run the user-provided function on it. Because of | |
-- the universal quantification, the user will not be able to know which type | |
-- was wrapped, but only knows about the constraint ('c'), which is exactly the | |
-- same thing as inspecting the returned value of a function returning | |
-- 'impl Trait' in Rust. | |
inspect :: (forall a. c a => a -> b) -> Impl c -> b | |
inspect f (Impl a) = f a | |
-- An example very similar to what you’d have in Rust. The only difference is | |
-- that you need to annotate the return type with Impl to wrap the existential. | |
foo :: String -> Impl Show | |
foo x = Impl $ "Hello, " <> x | |
example :: IO () | |
example = inspect print $ foo "Roger" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment