Haskell中类型类中的异常约束
我有个问题。我正在尝试写一个文件系统管理的类类型。我从一个简单的类开始:Haskell中类型类中的异常约束,haskell,exception,io,filesystems,Haskell,Exception,Io,Filesystems,我有个问题。我正在尝试写一个文件系统管理的类类型。我从一个简单的类开始: import Control.Exception class (Monad m) => Commands m where --Similar to catch :: Exception e => IO a -> (e -> IO a) -> IO a fsCatch :: Exception e => m a -> (e -> m a)
import Control.Exception
class (Monad m) => Commands m where
--Similar to catch :: Exception e => IO a -> (e -> IO a) -> IO a
fsCatch
:: Exception e
=> m a
-> (e -> m a)
-> m a
setCurDir
:: FilePath
-> m ()
caughtError
:: Exception e
=> e
-> m ()
cd
:: FilePath
-> m ()
cd path = fsCatch (setCurDir path) caughtError
有一个函数cd可以在目录上移动。它尝试使用setCurDir更改目录,但需要实现,如果出现问题,它将调用caughtError函数
我想创建两个实例:用于IO和我的“玩具”文件系统数据类
对于IO来说,它看起来像这样:
instance Commands IO where
fsCatch = catch
setCurDir = setCurrentDirectory
caughtError ex | isDoesNotExistError ex = putStrLn "No such directory"
| etc...
IO有自己的IOError。我将为我的玩具文件系统生成MyFSError。但是我在我的type类中遇到了这个错误:
* Could not deduce (Exception e0) arising from a use of `fsCatch'
from the context: Commands m
bound by the class declaration for `Commands'
at D:\\University\FP\hw3-olegggatttor\hw3\src\Commands.hs:6:20-27
The type variable `e0' is ambiguous
These potential instances exist:
instance Exception ErrorCall -- Defined in `GHC.Exception'
instance Exception ArithException
-- Defined in `GHC.Exception.Type'
instance Exception SomeException -- Defined in `GHC.Exception.Type'
...plus 10 others
...plus three instances involving out-of-scope types
(use -fprint-potential-instances to see them all)
* In the expression: fsCatch (setCurDir path) catchedError
In an equation for `cd':
cd path = fsCatch (setCurDir path) catchedError
|
61 | cd path = fsCatch (setCurDir path) catchedError
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Failed, no modules loaded.
问题在哪里?如何解决?必须使用类类型。问题是cd需要异常e约束来调用fsCatch和caughtError。然而,cd的定义或它的类型签名中没有提供这样的约束。见鬼,cd的类型签名中甚至没有提到类型e
对于这个特定的类型类,看起来您希望monad m确定异常类型e。如果m~IO,则e~IOError;如果m~ToyFilesystem,则e~MyFSError。这可以使用函数依赖项或关联的类型族来实现。我认为大家的共识可能是,关联类型族方法是更现代、更直接的方法,因此应该是首选方法,但我将向大家展示这两种方法
对于函数依赖项实现,您可以重新参数化类型类以包含异常类型e,并添加注释m->e,该注释指示monad的类型唯一地确定异常的类型:
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE MultiParamTypeClasses #-}
class (Monad m, Exception e) => Commands m e | m -> e where
fsCatch :: m a -> (e -> m a) -> m a
setCurDir :: FilePath -> m ()
caughtError :: e -> m ()
cd :: FilePath -> m ()
cd path = fsCatch (setCurDir path) caughtError
IO实例如下所示:
instance Commands IO IOError where
fsCatch = catch
setCurDir = setCurrentDirectory
caughtError ex | isDoesNotExistError ex = putStrLn "No such directory"
请注意,在这个类中实际上根本不需要Monad m或Exception e约束,因此将类声明切换为:
class Commands m e | m -> e where
...
很好。我认为这是一个更好的实践——在类声明中不应该出现Monad或异常约束——但这是另一个SO问题的答案
对于关联的类型族实现,可以向类添加类型族声明,该声明将monad显式映射到其异常类型:
class (Monad m, Exception (E m)) => Commands m where
type E m -- exception type for monad m
fsCatch :: m a -> (E m -> m a) -> m a
setCurDir :: FilePath -> m ()
caughtError :: E m -> m ()
cd :: FilePath -> m ()
cd path = fsCatch (setCurDir path) caughtError
同样,这里不需要约束,您可以编写:
class Commands m where
...
实例然后为monad m指定类型em:
功能依赖关系的完整示例:
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeSynonymInstances #-}
import Control.Exception
import System.Directory
import System.IO.Error
class Commands m e | m -> e where
fsCatch :: m a -> (e -> m a) -> m a
setCurDir :: FilePath -> m ()
caughtError :: e -> m ()
cd :: FilePath -> m ()
cd path = fsCatch (setCurDir path) caughtError
instance Commands IO IOError where
fsCatch = catch
setCurDir = setCurrentDirectory
caughtError ex | isDoesNotExistError ex = putStrLn "No such directory"
对于关联的类型族:
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TypeFamilies #-}
import Control.Exception
import System.Directory
import System.IO.Error
class Commands m where
type E m
fsCatch :: m a -> (E m -> m a) -> m a
setCurDir :: FilePath -> m ()
caughtError :: E m -> m ()
cd :: FilePath -> m ()
cd path = fsCatch (setCurDir path) caughtError
instance Commands IO where
type E IO = IOError
fsCatch = catch
setCurDir = setCurrentDirectory
caughtError ex | isDoesNotExistError ex = putStrLn "No such directory"
问题是cd需要一个异常e约束来调用fsCatch和caughtError。然而,cd的定义或它的类型签名中没有提供这样的约束。见鬼,cd的类型签名中甚至没有提到类型e
对于这个特定的类型类,看起来您希望monad m确定异常类型e。如果m~IO,则e~IOError;如果m~ToyFilesystem,则e~MyFSError。这可以使用函数依赖项或关联的类型族来实现。我认为大家的共识可能是,关联类型族方法是更现代、更直接的方法,因此应该是首选方法,但我将向大家展示这两种方法
对于函数依赖项实现,您可以重新参数化类型类以包含异常类型e,并添加注释m->e,该注释指示monad的类型唯一地确定异常的类型:
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE MultiParamTypeClasses #-}
class (Monad m, Exception e) => Commands m e | m -> e where
fsCatch :: m a -> (e -> m a) -> m a
setCurDir :: FilePath -> m ()
caughtError :: e -> m ()
cd :: FilePath -> m ()
cd path = fsCatch (setCurDir path) caughtError
IO实例如下所示:
instance Commands IO IOError where
fsCatch = catch
setCurDir = setCurrentDirectory
caughtError ex | isDoesNotExistError ex = putStrLn "No such directory"
请注意,在这个类中实际上根本不需要Monad m或Exception e约束,因此将类声明切换为:
class Commands m e | m -> e where
...
很好。我认为这是一个更好的实践——在类声明中不应该出现Monad或异常约束——但这是另一个SO问题的答案
对于关联的类型族实现,可以向类添加类型族声明,该声明将monad显式映射到其异常类型:
class (Monad m, Exception (E m)) => Commands m where
type E m -- exception type for monad m
fsCatch :: m a -> (E m -> m a) -> m a
setCurDir :: FilePath -> m ()
caughtError :: E m -> m ()
cd :: FilePath -> m ()
cd path = fsCatch (setCurDir path) caughtError
同样,这里不需要约束,您可以编写:
class Commands m where
...
实例然后为monad m指定类型em:
功能依赖关系的完整示例:
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeSynonymInstances #-}
import Control.Exception
import System.Directory
import System.IO.Error
class Commands m e | m -> e where
fsCatch :: m a -> (e -> m a) -> m a
setCurDir :: FilePath -> m ()
caughtError :: e -> m ()
cd :: FilePath -> m ()
cd path = fsCatch (setCurDir path) caughtError
instance Commands IO IOError where
fsCatch = catch
setCurDir = setCurrentDirectory
caughtError ex | isDoesNotExistError ex = putStrLn "No such directory"
对于关联的类型族:
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TypeFamilies #-}
import Control.Exception
import System.Directory
import System.IO.Error
class Commands m where
type E m
fsCatch :: m a -> (E m -> m a) -> m a
setCurDir :: FilePath -> m ()
caughtError :: E m -> m ()
cd :: FilePath -> m ()
cd path = fsCatch (setCurDir path) caughtError
instance Commands IO where
type E IO = IOError
fsCatch = catch
setCurDir = setCurrentDirectory
caughtError ex | isDoesNotExistError ex = putStrLn "No such directory"
非常感谢你!函数依赖性看起来是解决我的问题的完美方案!很好的回答非常感谢!函数依赖性看起来是解决我的问题的完美方案!很好的回答