Search code examples
haskellhaskell-polysemy

Unable to raiseUnder


I try to use raiseUnder (with polysemy 1.6.0) to introduce effects to use other interpreters, such that:

{-# LANGUAGE TemplateHaskell #-}
{-# OPTIONS_GHC -fplugin=Polysemy.Plugin #-}

module Memoization where

import Data.Kind
import qualified Data.Map.Strict as M
import Polysemy
import Polysemy.State

data Memoization (k :: Type) (v :: Type) (m :: Type -> Type) (a :: Type) where
  FetchMemoized :: k -> Memoization k v m v

makeSem ''Memoization

runMemoizationState ::
  forall k v r.
  ( Ord k,
    Members
      '[State (M.Map k v)]
      r
  ) =>
  (k -> Sem r v) ->
  InterpreterFor (Memoization k v) r
runMemoizationState f = interpret $ \case
  FetchMemoized k -> do
    memoized <- get @(M.Map k v)
    case memoized M.!? k of
      Just memoizedResult -> return memoizedResult
      Nothing -> do
        result <- f k
        modify' $ M.insert k result
        return result

runMemoizationState' ::
  forall k v r.
  ( Ord k
  ) =>
  (k -> Sem r v) ->
  InterpreterFor (Memoization k v) r
runMemoizationState' f =
  evalState mempty
    . runMemoizationState f
    . raiseUnder @(State (M.Map k v)) @(Memoization k v) @r

While I expect it to simply inject a new effect:

raiseUnder :: forall e2 e1 r a. Sem (e1 ': r) a -> Sem (e1 ': (e2 ': r)) a

I get this error:

Sem r a -> Sem (WithTactics e f (Sem rInitial) r) (f a)
Memoization.hs:46:7-55: error:
    * Occurs check: cannot construct the infinite type:
        r ~ State (M.Map k v) : r
      Expected type: Sem (Memoization k v : r) a
                     -> Sem (Memoization k v : r) a
        Actual type: Sem (Memoization k v : r) a
                     -> Sem (Memoization k v : State (M.Map k v) : r) a
    * In the second argument of `(.)', namely
        `raiseUnder @(State (M.Map k v)) @(Memoization k v) @r'
      In the second argument of `(.)', namely
        `runMemoizationState f
           . raiseUnder @(State (M.Map k v)) @(Memoization k v) @r'
      In the expression:
        evalState mempty
          . runMemoizationState f
              . raiseUnder @(State (M.Map k v)) @(Memoization k v) @r
    * Relevant bindings include
        f :: k -> Sem r v
          (bound at Memoization.hs:43:22)
        runMemoizationState' :: (k -> Sem r v)
                                -> InterpreterFor (Memoization k v) r
          (bound at Memoization.hs:43:1)
   |
46 |     . raiseUnder @(State (M.Map k v)) @(Memoization k v) @r
   |       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Solution

  • As mentioned by @Georgi Lyubenov, the issue was that my function f, in runMemoizationState, neededthe State effect, once decoupled, it works:

    runMemoizationState ::
      forall k v r.
      Ord k =>
      (k -> Sem r v) ->
      InterpretersFor '[Memoization k v, State (M.Map k v)] r
    runMemoizationState f =
      evalState mempty
        . run'
      where
        run' :: InterpreterFor (Memoization k v) (State (M.Map k v) ': r)
        run' = interpret $ \case
          FetchMemoized k -> do
            memoized <- get @(M.Map k v)
            case memoized M.!? k of
              Just memoizedResult -> return memoizedResult
              Nothing -> do
                result <- raise $ f k
                modify' $ M.insert k result
                return result
    
    runMemoizationState' ::
      forall k v r.
      ( Ord k
      ) =>
      (k -> Sem r v) ->
      InterpreterFor (Memoization k v) r
    runMemoizationState' f =
      runMemoizationState f
        . raiseUnder @(State (M.Map k v)) @(Memoization k v) @r