编写一个通用函数,其中包含两种参数类型的变体
这是这个问题的后续问题。 我想我在Haskell中误解了一下什么类型的内容,希望能够更好地表达这个问题:
我想要一个可以用两个参数调用的函数。 这些参数必须是不同的类型。 例如,一个是字符串,另一个是整数。
考虑这个应用程序:
combine "100" 500 -- results in 100500
combine 100 "500" -- results in 100500
combine 100 500 -- raises an exception
combine "100" "500" -- raises an exception
写一个具体的实现并不是一个问题,但是,对我而言,给这个函数一个合适的签名是一个问题。
我也有兴趣了解是否有更通用的解决方案(即不需要指定具体类型,但只规定类型不同)。例如,您可以使用此函数来将输入“修复”到其他功能,如果它可以通过置换参数来修复。
谢谢!
编辑:
下面是我期待它在Erlang中做的一个不精确的副本......嗯,我希望这是有道理的,因为它应该非常相似......
combine([String], Int)->
io:fwrite("~s~w~n", [[String], Int]);
combine(Int, [String])->
combine([String], Int).
Sjoerd打败了我,但我更喜欢我的解决方案,所以我会发布它。
{-# LANGUAGE MultiParamTypeClasses, TypeSynonymInstances, FlexibleInstances #-}
module Foo where
class Combinable a b where
combine :: a -> b -> Int
instance Combinable Int String where
combine a b = read (show a ++ b)
instance Combinable String Int where
combine a b = read (a ++ show b)
由于这不包含一个Combinable aa
实例,因此尝试使用它是编译时错误而不是运行时错误。
这不是100%清楚为什么你想要这个。 我想出了其他人没有提到的一种可能性,那就是您只需要与订单无关的功能应用程序。 这可以通过“记录应用程序”习语来实现。 例如,你可能会写这样的东西:
data Argument = Argument { name :: String, age :: Int }
instance Default Argument where def = Argument def def
combine Argument { name = n, age = a } = name ++ " is " ++ show age ++ " years old"
然后你可以用命名参数调用它:
combine def { name = "Daniel", age = 3 }
combine def { age = 3, name = "Daniel" }
与检查类型不相等相比,名称甚至更好一些,因为您可以使用相同类型的多个参数而没有歧义。
data Name = Name { first, middle, last :: String }
instance Default Name where def = Name def def def
esquire n@(Name { last = l }) = n { last = l ++ ", Esquire" }
例如,您可以调用这两种方法:
esquire def { first = "Daniel", middle = "M.", last = "Wagner" }
esquire def { last = "Wagner", first = "Daniel" }
其他答案是“写一个(稍微丑陋的)类”和“通过总和类型统一类型”。 我会做一个非常非常Haskelly的建议,并提醒大家,如果您要求,Haskell确实有动态打字。
在运行时,只要问什么类型,并使您的操作不同,每种类型。 这可以使用Data.Typeable模块完成。
例如:
import Data.Typeable
import Data.Data
combine :: (Typeable a, Typeable b) => a -> b -> Int
combine a b
| typeOf a == strTy && typeOf b == intTy =
case (cast a, cast b) of
(Just str,Just i) -> read $ str ++ show (i :: Int)
| typeOf a == intTy && typeOf b == strTy =
case (cast a, cast b) of
(Just i,Just str) -> read $ show (i :: Int) ++ str
| otherwise = error "You said you wanted an exception..."
where
strTy = typeOf ""
intTy = typeOf (undefined :: Int)
测试运行显示:
> combine "100" (500 :: Int)
100500
如果你想摆脱例外,那么太棒了! 我们可以使用Maybe monad来清理代码:
combine2 :: (Typeable a, Typeable b) => a -> b -> Maybe Int
combine2 a b
| typeOf a == strTy && typeOf b == intTy = do
a' <- cast a
b' <- cast b
return $ read $ a' ++ show (b' :: Int)
| typeOf a == intTy && typeOf b == strTy = do
a' <- cast a
b' <- cast b
return $ read $ show (a' :: Int) ++ b'
| otherwise = Nothing
where
strTy = typeOf ""
intTy = typeOf (undefined :: Int)
还有更多的产出只是为了它:
> combine2 "500" (5 :: Int)
Just 5005
> combine (5 :: Int) "500"
5500
> combine2 (5 :: Int) "500"
Just 5500
> combine "500" "300"
*** Exception: You said you wanted an exception...
> combine2 "500" "300"
Nothing
就是这样! 我们可以添加多少种我们想要的组合类型,只需在最后的otherwise
守卫之前插入您想要的操作即可。
上一篇: Write a generic function with exactly two variations of types of parameters