Could not deduce (a ~ Int) bei einfacher Funktion



  • Hallo,

    ich lerne gerade etwas Haskell, ich bin schwer durch imperative Programmierung geschädigt und tu mich nun relativ schwer.

    Eine Funktion soll eine Liste von Ganzzahlen entgegennehmen, einen Notenschnitt daraus berechnen und als Ausgabe einen Kommentar dazu abgeben.

    Ich habe das so probiert:

    grades :: (Num a) => [a] -> String
    grades m
    	| gs < 1 = "Excellent"
    	| gs < 2 = "Very good"
    	| gs < 3 = "Good"
    	| gs < 4 = "Okay... What are we going to eat tonight?"
    	| otherwise = "Bad"
    	where gs = (sum m) / (length m)
    

    Aufruf:

    print(grades [2,2,2,1,1])
    

    Leider erhalte ich lediglich folgende Meldung:

    Could not deduce (a ~ Int)
        from the context (Num a)
          bound by the type signature for grades :: Num a => [a] -> String
          at helloworld.hs:(36,1)-(42,39)
          `a' is a rigid type variable bound by
              the type signature for grades :: Num a => [a] -> String
              at helloworld.hs:36:1
        In the return type of a call of `length'
        In the second argument of `(/)', namely `(length m)'
        In the expression: (sum m) / (length m)
    

    Offenbar passt ihm etwas in der Typinferenz nicht... allerdings verstehe / sehe ich nicht genau woran es liegt...

    Wer kann mir helfen?



  • -- am besten guckst du dir die Typen an
    Prelude> :t (/)
    (/) :: Fractional a => a -> a -> a
    Prelude> :t div
    div :: Integral a => a -> a -> a
    Prelude> :t length
    length :: [a] -> Int
    Prelude> :t sum
    sum :: Num a => [a] -> a
    
    -- wenn die Typen zusammen passen, dann funktioniert es auch
    Prelude> let xs = [1..5] :: [Int]
    Prelude> :t xs
    xs :: [Int]
    Prelude> :t length xs
    length xs :: Int
    Prelude> :t sum xs
    sum xs :: Int
    Prelude> sum xs `div` length xs
    3
    
    -- nochmal
    Prelude> let xs = [1..5]
    Prelude> :t xs
    xs :: [Integer]                  -- Integer statt Int
    Prelude> :t length xs
    length xs :: Int                 -- Int
    Prelude> :t sum xs
    sum xs :: Integer                -- Integer
    Prelude> sum xs `div` length xs  -- Int `div` Integer -> aua
    
    <interactive>:87:14:
        Couldn't match expected type `Integer' with actual type `Int'
        In the return type of a call of `length'
        In the second argument of `div', namely `length xs'
        In the expression: sum xs `div` length xs
    Prelude> sum xs `div` (fromIntegral $ length xs)
    3
    
    -- und jetzt für Fractional
    Prelude> sum xs / 1.0
    
    <interactive>:101:8:
        No instance for (Fractional Integer) arising from a use of `/'
        Possible fix: add an instance declaration for (Fractional Integer)
        In the expression: sum xs / 1.0
        In an equation for `it': it = sum xs / 1.0
    Prelude> (sum xs) / 1.0
    
    <interactive>:102:10:
        No instance for (Fractional Integer) arising from a use of `/'
        Possible fix: add an instance declaration for (Fractional Integer)
        In the expression: (sum xs) / 1.0
        In an equation for `it': it = (sum xs) / 1.0
    Prelude> (fromIntegral $ sum xs) / 1.0
    15.0
    Prelude> (fromIntegral $ sum xs) / (length xs)
    
    <interactive>:104:25:
        No instance for (Fractional Int) arising from a use of `/'
        Possible fix: add an instance declaration for (Fractional Int)
        In the expression: (fromIntegral $ sum xs) / (length xs)
        In an equation for `it': it = (fromIntegral $ sum xs) / (length xs)
    Prelude> (fromIntegral $ sum xs) / (fromIntegral $ length xs)
    3.0
    

    Führt zu:

    grades :: (Integral a) => [a] -> String
    grades m
        | gs < 1 = "Excellent"
        | gs < 2 = "Very good"
        | gs < 3 = "Good"
        | gs < 4 = "Okay... What are we going to eat tonight?"
        | otherwise = "Bad"
        where gs = (fromIntegral $ sum m) / (fromIntegral $ length m)
    


  • Oder auch mit genericLength aus Data.List :

    grades :: Integral a => [a] -> String 
    grades = (text !!) . fromIntegral . min 0 . mean
      where
        mean xs = (sum xs) `div` (genericLength xs)
        text = [ "Excellent"
               , "Very good"
               , "Good"
               , "Okay... What are we going to eat tonight?"
               ] ++ (repeat "Bad")
    


  • Vielen Dank für die Antworten, Frage geklärt 👍



  • Vielleicht noch ein kleiner Nachtrag:

    Es ist eine gute Strategie, die Signatur erstmal wegzulassen, und gucken, was dabei herumkommt.
    Die length-Funktion ist seltsamerweise als (output) -> Int definiert, was bei diesen und ähnlichen Aufgaben vermutlich öfter als ärgerliches Hindernis bei Anfängern auftauchen wird. Rein aus technischer Sicht ist irgendwie auch nicht ganz klar, was daran so schwer scheint, eine "Int" Zahl in die FPU zu schmeissen.

    Wie auch immer, wenn man die length-Funktion (notgedrungen) selbst schreibt, und dabei den Out in eine höhere (oder speziellere) Klasse hebt (z.B. Num a), also z.B. mit

    mylength :: Num a => [a] -> a
    mylength [] = 0 
    usw.
    

    Dann verschwindet das obige Problem erstmal.

    Man kann sich dann mit :info grades die dazu passende Signatur angucken, und die neue Funktion mit
    mit grades ... oder putStrLn (grades...) aufrufen.
    (und sollte dann auch die Fehlermeldungen verstehen 😉 )


Anmelden zum Antworten