Haskell 中 class 类型的异常约束
Exception constraint in type class in Haskell
我有问题。我正在尝试为文件系统管理写下 class 类型。我从一个简单的 class:
开始
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 和我的 'toy' 文件系统数据 class。
对于 IO,它将看起来像这样:
instance Commands IO where
fsCatch = catch
setCurDir = setCurrentDirectory
caughtError ex | isDoesNotExistError ex = putStrLn "No such directory"
| etc...
IO 有自己的 IOError。我将为我的玩具文件系统创建 MyFSError。但是我的类型 class:
出现了这个错误
* 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.
问题出在哪里,如何解决?必须使用class类型。
问题是 cd
需要 Exception e
约束来调用 fsCatch
和 caughtError
。但是,cd
的定义或其类型签名中没有提供此类约束的任何内容。哎呀,类型 e
甚至没有 在 cd
.
的类型签名中提到
对于这个特定类型 class,您似乎希望 monad m
确定异常类型 e
。如果 m ~ IO
,则 e ~ IOError
,如果 m ~ ToyFilesystem
,则 e ~ MyFSError
。这可以使用函数依赖或关联的类型族来完成。我认为共识可能是关联类型族方法是更现代、更直接的方法,因此应该是首选方法,但我会向你们展示这两种方法。
对于功能依赖实现,您重新参数化类型 class 以包含异常类型 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"
请注意,您实际上根本不需要此 class 中的 Monad m
或 Exception e
约束,因此请将 class 声明切换为:
class Commands m e | m -> e where
...
工作正常。我认为这是更好的做法——Monad
或 Exception
约束不应出现在你的 class 声明中的任何地方——但这是另一个 SO 问题的答案。
对于关联的类型族实现,您将类型族声明添加到 class,明确地将 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
:
指定类型 E m
instance Commands IO where
type E IO = IOError
fsCatch = catch
setCurDir = setCurrentDirectory
caughtError ex | isDoesNotExistError ex = putStrLn "No such directory"
函数依赖的完整示例:
{-# 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"
我有问题。我正在尝试为文件系统管理写下 class 类型。我从一个简单的 class:
开始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 和我的 'toy' 文件系统数据 class。
对于 IO,它将看起来像这样:
instance Commands IO where
fsCatch = catch
setCurDir = setCurrentDirectory
caughtError ex | isDoesNotExistError ex = putStrLn "No such directory"
| etc...
IO 有自己的 IOError。我将为我的玩具文件系统创建 MyFSError。但是我的类型 class:
出现了这个错误 * 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.
问题出在哪里,如何解决?必须使用class类型。
问题是 cd
需要 Exception e
约束来调用 fsCatch
和 caughtError
。但是,cd
的定义或其类型签名中没有提供此类约束的任何内容。哎呀,类型 e
甚至没有 在 cd
.
对于这个特定类型 class,您似乎希望 monad m
确定异常类型 e
。如果 m ~ IO
,则 e ~ IOError
,如果 m ~ ToyFilesystem
,则 e ~ MyFSError
。这可以使用函数依赖或关联的类型族来完成。我认为共识可能是关联类型族方法是更现代、更直接的方法,因此应该是首选方法,但我会向你们展示这两种方法。
对于功能依赖实现,您重新参数化类型 class 以包含异常类型 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"
请注意,您实际上根本不需要此 class 中的 Monad m
或 Exception e
约束,因此请将 class 声明切换为:
class Commands m e | m -> e where
...
工作正常。我认为这是更好的做法——Monad
或 Exception
约束不应出现在你的 class 声明中的任何地方——但这是另一个 SO 问题的答案。
对于关联的类型族实现,您将类型族声明添加到 class,明确地将 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
:
E m
instance Commands IO where
type E IO = IOError
fsCatch = catch
setCurDir = setCurrentDirectory
caughtError ex | isDoesNotExistError ex = putStrLn "No such directory"
函数依赖的完整示例:
{-# 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"