如何使用灵活类型变量将monadic函数作为参数传递?

joh*_*wie 2 monads haskell typeclass rank-n-types

为潜在的模糊问题标题道歉 - 我不确定如何表达它,因为我对问题的解释很差.

基本上,我如何进行以下编译?:-P

{-# LANGUAGE MultiParamTypeClasses #-}

class (Monad m) => MyClass m a where
  valM :: m (Maybe a)
  val  :: m a

f :: (MyClass m a) => (m a -> IO a) -> IO (a, Maybe a)
f g = do
  x <- g val
  yM <- g valM
  return (x, yM)
Run Code Online (Sandbox Code Playgroud)

GHC(V8.2.2)抱怨a是刚性类型的变量,似乎无法应付这个想法(g val)(g valM)可能产生不同类型的值.我尝试过RankNTypes但无济于事.

是否有一个我可以用来帮助编译器的扩展,或者从类型推断的角度来看,我正在尝试做什么概念?

Ale*_*ing 8

你是对的RankNTypes,但是你错过了forall.正确的类型f是:

f :: MyClass m a => (forall b. m b -> IO b) -> IO (a, Maybe a)
Run Code Online (Sandbox Code Playgroud)

...因为传递给的函数f必须适用于任何结果类型,并且它不应与a结果中的相关.


值得注意的是,这种函数也称为自然变换,自然变换包为这些函数提供了一个(~>)类型别名:

type (~>) f g = forall a. f a -> g a
Run Code Online (Sandbox Code Playgroud)

因此,使用该类型别名,您也可以这样写f:

f :: MyClass m a => (m ~> IO) -> IO (a, Maybe a)
Run Code Online (Sandbox Code Playgroud)