haskell 编写Genric函数,该函数在类型级别将参数长度可变的函数签名作为参数

jdgnovmf  于 2023-02-16  发布在  其他
关注(0)|答案(1)|浏览(128)

我想创建一个函数,它把函数作为变量参数,我对函数参数(它本身就是一个函数)的唯一了解是,它们被 Package 在同一个单子中。

myFunc :: forall arguments output m. 
                      MonadEffect m 
                   => (arguments -> m output)
                   ->  m (argument -> m output)
myFunc fn = do
            -- getting function's name using FFI and other stuff...
            pure fn

当我显式地指定参数长度时,我可以写这个函数,

-- for function with ONE argument
myFunc1 :: forall argument1 output m
                   . MonadEffect m 
                   => (argument1 -> m output)
                   -> m (argument1 -> m output)
myFunc1 fn = do 
              -- do something 
              pure fn

-- for function with TWO argument
myFunc2 :: forall argument1 argument2 output m. 
                   MonadEffect m => 
                   (argument1 -> argument2 -> m output) ->
                   m (argument1 -> argument2 ->  m output)
myFunc2 fn = do 
             -- do something 
             pure fn

如何在类型级别将argument1 -> argument2 -> m output写为argument -> m output

是否有任何类型/约束有助于这样做?
ps:我只是想弄清楚有没有可能

m1m5dgzv

m1m5dgzv1#

在Haskell中,可以定义计算函数返回类型的类型族:

type family Result f where
  Result (a -> b) = Result b
  Result b = b

以及其参数类型的类型级列表(按相反顺序):

type Arguments f = Arguments' '[] f
type family Arguments' args f where
  Arguments' args (a -> b) = Arguments' (a ': args) b
  Arguments' args b = args

这将允许您编写:

myFunc :: forall f arguments output m.
  ( Monad m
  , Result f ~ m output
  , Arguments f ~ arguments
  ) => f -> m f
myFunc fn = pure fn

这将使类型变量outputarguments在函数体中可用:

myFunc :: forall f arguments output m.
  ( MonadIO m
  , Result f ~ m output
  , Arguments f ~ arguments
  , Typeable arguments, Typeable output
  ) => f -> m f
myFunc fn = do
  liftIO . putStrLn $ "Result: " ++ show (typeRep (Proxy @output))
  liftIO . putStrLn $ "Arguments: " ++ show (typeRep (Proxy @arguments))
  pure fn

这可能适合也可能不适合您要做的事情,它可能在PureScript中工作也可能不工作。
不管怎样,这里有一个自包含的Haskell例子:

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}

import Control.Monad.IO.Class
import Data.Typeable

type family Result f where
  Result (a -> b) = Result b
  Result b = b

type Arguments f = Arguments' '[] f
type family Arguments' args f where
  Arguments' args (a -> b) = Arguments' (a ': args) b
  Arguments' args b = args

myFunc :: forall f arguments output m.
  ( MonadIO m
  , Result f ~ m output
  , Arguments f ~ arguments
  , Typeable arguments, Typeable output
  ) => f -> m f
myFunc fn = do
  liftIO . putStrLn $ "Result: " ++ show (typeRep (Proxy @output))
  liftIO . putStrLn $ "Arguments: " ++ show (typeRep (Proxy @arguments))
  liftIO . putStrLn $ ""
  pure fn

resultType :: forall f r. (Result f ~ r, Typeable r) => f -> String
resultType _ = show $ typeRep (Proxy @r)

main :: IO ()
main = do
  _ <- myFunc (pure . length :: String -> IO Int)
  _ <- myFunc ((\x y -> pure (x+y)) :: Double -> Double -> IO Double)
  putStrLn "done"

相关问题