danidiaz / dep-t

Dependency injection for records-of-functions.
http://hackage.haskell.org/package/dep-t
BSD 3-Clause "New" or "Revised" License
8 stars 2 forks source link

Pattern synonym version of `asCall` #23

Closed danidiaz closed 1 year ago

danidiaz commented 1 year ago

It's possible to implement but, sadly, it currently incurs in a spurious MonadFail constraint.

danidiaz commented 1 year ago

Actually, I've tested it again with GHC 9.2.4, and now it works fine!

{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE StandaloneKindSignatures #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE ViewPatterns #-}

module Main where

import Data.Kind

type Has :: ((Type -> Type) -> Type) -> (Type -> Type) -> Type -> Constraint
class Has r_ m env | env -> m where
  dep :: env -> r_ m

makeCaller :: forall env m . env -> forall r_ x. Has r_ m env => (r_ m -> x) -> x
makeCaller env = \f -> f (dep env)

pattern Call :: forall env m . (forall r_ x. Has r_ m env => (r_ m -> x) -> x) -> env
pattern Call {call} <- (makeCaller -> call)
{-# COMPLETE Call #-}

newtype Logger m = Logger { logg :: String -> m () }

makeLogger :: Logger IO
makeLogger = Logger putStrLn 

newtype Foo m = Foo { foo :: Int -> m () }

data Env m = Env (Logger m)
instance Has Logger m (Env m) where
    dep (Env x) = x

makeFoo :: (Monad m, Has Logger m env) => env -> Foo m 
makeFoo (Call {call}) = Foo \i -> do
    call logg "logg" 
    pure ()

main :: IO ()
main = do
    Foo {foo} <- pure $ makeFoo (Env makeLogger)
    foo 5

Now the questions are

danidiaz commented 1 year ago

Possible implementation here.

danidiaz commented 1 year ago

Another possible pattern whose result we use as ther first argument of functions, instead of preceding them:

pattern Dep :: forall env m . (forall r_ . Has r_ m env => r_ m) -> env
pattern Dep δ <- ((dep :: env -> forall r_ . Has r_ m env => r_ m) -> δ)
{-# COMPLETE Dep #-}
{-# INLINABLE Dep #-}
danidiaz commented 1 year ago

Added in 2f59f87eb3f01f1e9a8e877fd9dcd91a5f64c534, although Dep was left out for now.