Skip to content

Instantly share code, notes, and snippets.

@hadronized
Created May 6, 2021 21:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hadronized/e270747f88c4461ef0d9f8bdae1c7824 to your computer and use it in GitHub Desktop.
Save hadronized/e270747f88c4461ef0d9f8bdae1c7824 to your computer and use it in GitHub Desktop.
{-# 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