Write a generic function with exactly two options for parameter types

This is the next question of this . I think I misunderstood which type is for Haskell, so hopefully a better question is posed:

I want to have a function that can be called with exactly two arguments. These arguments must be of different types. For example, one is a string and the other is an integer.

Consider this application:

combine "100" 500 -- results in 100500
combine 100 "500" -- results in 100500
combine 100 500 -- raises an exception
combine "100" "500" -- raises an exception

It is not difficult to write a specific implementation; for me, however, the problem is to give this function the correct signature.

I would also be interested to know if there is a more general solution (that is, it does not require specifying specific types, but only prescribes that the types be different. So, for example, you could use this function to “capture” the input data for other functions if they can be fixed by rearranging the arguments.

Thank!

EDIT:

below is an inaccurate copy of what I expected from her in Erlang ... well, I hope this makes sense, as it should be very similar ...

combine([String], Int)->
    io:fwrite("~s~w~n", [[String], Int]);

combine(Int, [String])->
    combine([String], Int).
+5
source share
8 answers

Sjoerd beat me, but I prefer my decision, so I will send it anyway.

{-# 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)

Since this does not include the instance Combinable a a, trying to use it is a compile-time error, not a run-time error.

+7
source

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" }
+6

" ( ) " " ". -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, :

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.

+6

, . Haskell , .

, OverlappingInstances multi-parameter type, , .

+3

, , :

{-# LANGUAGE MultiParamTypeClasses, TypeSynonymInstances, FlexibleInstances #-}

class Combine a b where 
  combine :: a -> b -> String

instance Combine a a where
  combine = error "Types are the same"

instance (Show a, Show b) => Combine a b where
  combine a b = show a ++ show b
+3

:

{-# FlexibleInstances, TypeSynonymInstances #-}

class IntOrString a where
  toString :: a -> String
  typeID :: a -> Int

instance IntOrString String where
  toString s = s
  typeID _ = 0    

instance IntOrString Int where
  toString x = show x
  typeID _ = 1    

combine a b | typeID a + typeID b == 1 = toString a ++ toString b
combine _ _ = error "WTF?!?"

combine "100" (500::Int) --type needed because of monomorphism restriction
+2

, undefined haskell.

data IntAndString = TypeA Int String | TypeB String Int

combine IntAndString -> string
combine TypeA(n s) = show n ++ s
combine TypeB(s n) = s ++ show n

combine TypeA(Int String)

combine TypeB(String Int)
+1

, , , , .

As long as functions don't have more ranked types, you don't need to. Haskell will infer the type for you.

However, I feel that what you want does not make much sense in Haskell, where code and data are strictly separated¹, as well as runtime and compilation time, unlike Lisp. What will use for combine?

¹ Of course, functions are data in a sense, but they are just opaque constants. You cannot manipulate a function at runtime.

+1
source

All Articles