我有一个函数,它接受account
并根据它是浮点数,双精度数还是整数执行不同的操作。我发现这样做的唯一方法是使用类型类,但这看起来非常难看。有没有办法匹配任何类型的数字,所以做不同的事情取决于3个Num实例中的哪一个?像
Num
而不是
myFun :: (Num a) => a -> T
myFun n = case n of
n :: Int -> something for ints
n :: Float -> something for floats
n :: Double -> something for doubles
包含class MyClass a where
myFun :: (Num a) => a -> T
,Int
,Float
的实例?
答案 0 :(得分:7)
根据我的阅读,这是类型类的教科书应用程序:
module Main where
class Num a => Funnable a where
myFun :: a -> a
instance Funnable Int where
myFun = id
instance Funnable Float where
myFun = (+ 2)
instance Funnable Double where
myFun = (+ 1)
main :: IO ()
main = do
print $ myFun (1 :: Int)
print $ myFun (1 :: Float)
print $ myFun (1 :: Double)
给出:
$ stack exec example
1
3.0
2.0
如果使用不正确,类型类还会为您提供编译时类型错误。例如,假设我们跳过Double
的实例声明:
src/Main.hs:14:13: error:
• No instance for (Funnable Double) arising from a use of ‘myFun’
• In the second argument of ‘($)’, namely ‘myFun (1 :: Double)’
In a stmt of a 'do' block: print $ myFun (1 :: Double)
In the expression: do { print $ myFun (1 :: Double) }
答案 1 :(得分:6)
你可以建模这样的东西:
data MyNum
= MDouble Double
| MInt Int
| MFloat Float
deriving (Show,Eq,Ord)
handle :: MyNum -> IO ()
handle (MDouble x) = putStrLn "This is double"
handle (MFloat x) = putStrLn "This is float"
handle (MInt x) = putStrLn "This is int"
事实上,library bson中使用了类似的抽象类型来为Value
类型建模。
答案 2 :(得分:3)
另一个,不可否认的是这样做是不错的:
import Data.Typeable
myFunc :: (Typeable a, Num a, Show a) => a -> String
myFunc x
| typeOf x == typeOf (0:: Int) = "Its an Int: " ++ show x
| typeOf x == typeOf (0:: Float) = "Its a Float: " ++ show x
| typeOf x == typeOf (0:: Double) = "Its a Double: " ++ show x
这个解决方案的弱点是,Num a
的实例比上述函数中使用的实例多。因此,在某个时刻,某人会例如致电myFunc 42
并感到惊讶,这是一个例外,因为没有案件处理Integer
,这也是Num a
。
相比之下,使用自己的类型类可以缩小可用类型的范围,从而有助于防止异常。
*主> myFunc(42 :: Int)
"它是一个Int:42"
*主> myFunc(42 :: Float)
"它的浮动:42.0"
*主> myFunc(42 :: Double)
"它是双人:42.0"
*主> myFunc 42
" ***异常:Main.hs:(11,1) - (14,66):函数myFunc中的非详尽模式