Search code examples
listhaskellfunctional-programmingpercentagesimilarity

haskell program to find the similarity between two lists


Alright, So I'm new to Haskell and I want to write a program where I take two lists and find the similarity (number of common items/number of items) between them. This is What I have so far:

fun2 :: [Int]->[Int]->Float

fun2 [] xs2 = 0
fun2 xs1 xs2 = if head xs1 == head xs2 then (1/length xs2) + fun2 tail xs1 xs2
else if head xs1 /= head xs2 then fun2 xs1 tail xs2
else fun2 tail xs1 xs2

main = fun2 [1,2,3,4] [3,4,5,6]

So let me explain what I'm trying to do, I defined my function to take two lists of integers and output a float (the similarity percentage). then I write my function, the base case is when the first list is empty, while the function will compare each element of the first list with each element of the second one, and if it finds a match it will add 1 then divide by the size to get the percentage. However, when I run this code I get a lot of errors:

main.hs:4:45: error:
• Couldn't match expected type ‘Float’ with actual type ‘Int’
• In the expression: (1 / length xs2) + fun2 tail xs1 xs2
  In the expression:
    if head xs1 == head xs2 then
        (1 / length xs2) + fun2 tail xs1 xs2
    else
        if head xs1 /= head xs2 then
            fun2 xs1 tail xs2
        else
            fun2 tail xs1 xs2
  In an equation for ‘fun2’:
      fun2 xs1 xs2
        = if head xs1 == head xs2 then
              (1 / length xs2) + fun2 tail xs1 xs2
          else
              if head xs1 /= head xs2 then
                  fun2 xs1 tail xs2
              else
                  fun2 tail xs1 xs2

main.hs:4:62: error:
• Couldn't match expected type ‘[Int] -> Int’
              with actual type ‘Float’
• The function ‘fun2’ is applied to three arguments,
  but its type ‘[Int] -> [Int] -> Float’ has only two
  In the second argument of ‘(+)’, namely ‘fun2 tail xs1 xs2’
  In the expression: (1 / length xs2) + fun2 tail xs1 xs2

main.hs:4:67: error:
• Couldn't match expected type ‘[Int]’
              with actual type ‘[a0] -> [a0]’
• Probable cause: ‘tail’ is applied to too few arguments
  In the first argument of ‘fun2’, namely ‘tail’
  In the second argument of ‘(+)’, namely ‘fun2 tail xs1 xs2’
  In the expression: (1 / length xs2) + fun2 tail xs1 xs2

main.hs:5:39: error:
• Couldn't match expected type ‘[Int] -> Float’
              with actual type ‘Float’
• The function ‘fun2’ is applied to three arguments,
  but its type ‘[Int] -> [Int] -> Float’ has only two
  In the expression: fun2 xs1 tail xs2
  In the expression:
    if head xs1 /= head xs2 then
        fun2 xs1 tail xs2
    else
        fun2 tail xs1 xs2

main.hs:5:48: error:
• Couldn't match expected type ‘[Int]’
              with actual type ‘[a1] -> [a1]’
• Probable cause: ‘tail’ is applied to too few arguments
  In the second argument of ‘fun2’, namely ‘tail’
  In the expression: fun2 xs1 tail xs2
  In the expression:
    if head xs1 /= head xs2 then
        fun2 xs1 tail xs2
    else
        fun2 tail xs1 xs2

main.hs:6:10: error:
• Couldn't match expected type ‘[Int] -> Float’
              with actual type ‘Float’
• The function ‘fun2’ is applied to three arguments,
  but its type ‘[Int] -> [Int] -> Float’ has only two
  In the expression: fun2 tail xs1 xs2
  In the expression:
    if head xs1 /= head xs2 then
        fun2 xs1 tail xs2
    else
        fun2 tail xs1 xs2

main.hs:6:15: error:
• Couldn't match expected type ‘[Int]’
              with actual type ‘[a2] -> [a2]’
• Probable cause: ‘tail’ is applied to too few arguments
  In the first argument of ‘fun2’, namely ‘tail’
  In the expression: fun2 tail xs1 xs2
  In the expression:
    if head xs1 /= head xs2 then
        fun2 xs1 tail xs2
    else
        fun2 tail xs1 xs2

main.hs:8:1: error:
• Couldn't match expected type ‘IO t0’ with actual type ‘Float’
• In the expression: main
  When checking the type of the IO action ‘main’

So Could you please tell me what I'm doing wrong here?


Solution

  • main.hs:4:45: error:
    • Couldn't match expected type ‘Float’ with actual type ‘Int’
    • In the expression: (1 / length xs2) + fun2 tail xs1 xs2
      …
    

    length returns an Int.

    For example, in GHCi:

    > :type length
    length :: Foldable t => t a -> Int
    
    > :set -XTypeApplications
    > :type length @[]
    length @[] :: [a] -> Int
    

    (Note that I write > to indicate the prompt. You can set your prompt to the same using :set prompt "> ", and :set prompt-cont "| " for multi-line input.)

    While (/) works on types that are in the set Fractional:

    > :type (/)
    (/) :: Fractional a => a -> a -> a
    
    > :info Fractional
    class Num a => Fractional a where
      (/) :: a -> a -> a
      recip :: a -> a
      fromRational :: Rational -> a
      {-# MINIMAL fromRational, (recip | (/)) #-}
        -- Defined in ‘GHC.Real’
    instance forall a k (b :: k).
             Fractional a =>
             Fractional (Const a b)
      -- Defined in ‘Data.Functor.Const’
    instance Fractional Float -- Defined in ‘GHC.Float’
    instance Fractional Double -- Defined in ‘GHC.Float’
    

    Int is not in Fractional:

    > :set -XFlexibleContexts
    > () :: (Fractional Int) => ()
    
    <interactive>:…:1: error:
        • No instance for (Fractional Int)
            arising from an expression type signature
        • In the expression: () :: (Fractional Int) => ()
          In an equation for ‘it’: it = () :: (Fractional Int) => ()
    

    So you need to convert the result of length from an Int to a Float with fromIntegral. This function can return any type that is in Num, and note in the output of :info Fractional above that class Num a => Fractional a means that Fractional is a subset of Num.

    > :type fromIntegral
    fromIntegral :: (Integral a, Num b) => a -> b
    
    > fromIntegral (length "abc") :: Float
    3.0
    

    In other words, you can write 1 / fromIntegral (length xs2) instead. Note the parentheses! That leads us to the next several error messages:

    main.hs:4:62: error:
    • Couldn't match expected type ‘[Int] -> Int’
                  with actual type ‘Float’
    • The function ‘fun2’ is applied to three arguments,
      but its type ‘[Int] -> [Int] -> Float’ has only two
      In the second argument of ‘(+)’, namely ‘fun2 tail xs1 xs2’
      …
    

    When you write fun2 tail xs1 xs2, that means “apply fun2 to three arguments: tail, xs1, and xs2”. You wanted to pass the result of tail xs1 as a single argument, so you need parentheses to group them together, i.e., fun2 (tail xs1) xs2. This is also the cause of the next error:

    main.hs:4:67: error:
    • Couldn't match expected type ‘[Int]’
                  with actual type ‘[a0] -> [a0]’
    • Probable cause: ‘tail’ is applied to too few arguments
      In the first argument of ‘fun2’, namely ‘tail’
      In the second argument of ‘(+)’, namely ‘fun2 tail xs1 xs2’
      …
    

    tail is a function of type [a] -> [a], but you are passing it as the first argument of fun2, whose first parameter is a value of type [Int]. The same error message repeats due to the same mistake in the other calls of fun2.

    For example, the next expression should read fun2 xs1 (tail xs2). The next error also has the same root cause, and additionally gives you a good hint about how to solve it:

    main.hs:6:10: error:
    • Couldn't match expected type ‘[Int] -> Float’
                  with actual type ‘Float’
    • The function ‘fun2’ is applied to three arguments,
      but its type ‘[Int] -> [Int] -> Float’ has only two
      In the expression: fun2 tail xs1 xs2
      …

    Finally, main must be an IO action, conventionally IO (). It may return a result of any type, that is, you may use IO t for any type t, and the result value will simply be discarded. However, you are currently passing a Float, the result of calling fun2, hence the mismatch:

    main.hs:8:1: error:
    • Couldn't match expected type ‘IO t0’ with actual type ‘Float’
    • In the expression: main
      When checking the type of the IO action ‘main’
    

    The solution is to use an IO action such as print (fun2 [1,2,3,4] [3,4,5,6]), which is equivalent to putStrLn (show (fun2 [1,2,3,4] [3,4,5,6])), both of which will convert a Float to a String using the debug-dump class Show, and return an IO action which will print the result to standard output when main is executed.

    GHC’s error messages aren’t always perfect, but all of these error messages fortunately contained enough information to solve your problem. You just need more practice reading them and understanding what they’re saying and how to proceed.