编写一个通用函数,其中包含两种参数类型的变体

这是这个问题的后续问题。 我想我在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守卫之前插入您想要的操作即可。

链接地址: http://www.djcxy.com/p/43321.html

上一篇: Write a generic function with exactly two variations of types of parameters

下一篇: Scala: How to define "generic" function parameters?