Domanda

Sto cercando di comporre una funzione del tipo (Floating a) => a -> a -> a con una funzione di tipo (Floating a) => a -> a per ottenere una funzione di tipo (Floating a) => a -> a -> a. Ho il seguente codice:

test1 :: (Floating a) => a -> a -> a
test1 x y = x

test2 :: (Floating a) => a -> a
test2 x = x

testBoth :: (Floating a) => a -> a -> a
testBoth = test2 . test1
--testBoth x y = test2 (test1 x y)

Tuttavia, quando compilo in GHCI, ottengo il seguente errore:

/path/test.hs:8:11:
    Could not deduce (Floating (a -> a)) from the context (Floating a)
      arising from a use of `test2'
                   at /path/test.hs:8:11-15
    Possible fix:
      add (Floating (a -> a)) to the context of
        the type signature for `testBoth'
      or add an instance declaration for (Floating (a -> a))
    In the first argument of `(.)', namely `test2'
    In the expression: test2 . test1
    In the definition of `testBoth': testBoth = test2 . test1
Failed, modules loaded: none.

Si noti che la versione commentata-out di compilazioni testBoth. La cosa strana è che se tolgo i vincoli (Floating a) da tutte le firme di tipo o se cambio test1 a basta prendere x invece di x e y, compila testBoth.

Ho cercato StackOverflow, Haskell wiki, Google, ecc e non ha trovato nulla di una restrizione alla composizione funzione relative a questa situazione particolare. Qualcuno sa perché questo sta accadendo?

È stato utile?

Soluzione

   \x y -> test2 (test1 x y)
== \x y -> test2 ((test1 x) y)
== \x y -> (test2 . (test1 x)) y
== \x -> test2 . (test1 x)
== \x -> (test2 .) (test1 x)
== \x -> ((test2 .) . test1) x
== (test2 .) . test1

Queste due cose non sono come ogni altro.

   test2 . test1
== \x -> (test2 . test1) x
== \x -> test2 (test1 x)
== \x y -> (test2 (test1 x)) y
== \x y -> test2 (test1 x) y

Altri suggerimenti

Non Sei problema non ha nulla a che fare con Floating, anche se il typeclass non rendere il vostro errore più difficile da capire. Prendere il sottostante Codice come esempio:

test1 :: Int -> Char -> Int
test1 = undefined

test2 :: Int -> Int
test2 x = undefined

testBoth = test2 . test1

Qual è il tipo di testBoth? Bene, prendiamo il tipo di (.) :: (b -> c) -> (a -> b) -> a -> c e girare la manovella per ottenere:

  1. b ~ Int (l'argomento di test2 unificato con il primo argomento di (.))
  2. c ~ Int (il risultato di test2 unificato con il risultato del primo argomento di (.))
  3. a ~ Int (test1 argomento 1 unificato con argomento 2 di (.))
  4. b ~ Char -> Int (risultato di test1 unificato con argomento 2 di (.))

Ma aspettate! tale tipo variabile, 'b' (4 #, Char -> Int), deve unificare con l'argomento di tipo test2 (1 #, Int). Oh No!

Come si dovrebbe fare? Una soluzione corretta è:

testBoth x = test2 . test1 x

Ci sono altri modi, ma considero questo il più leggibile.

Edit: Allora, qual è stato l'errore cercando di dirti? E 'stato detto che unificando Floating a => a -> a con Floating b => b richiede un instance Floating (a -> a) ... mentre questo è vero, davvero non voleva GHC per cercare di trattare una funzione come un numero in virgola mobile.

Il tuo problema non ha nulla a che fare con Floating, ma con il fatto che si vuole comporre una funzione con due argomenti e una funzione con un argomento in un modo che non TYPECHECK. Ti do un esempio in termini di una funzione composta reverse . foldr (:) [].

reverse . foldr (:) [] ha il tipo [a] -> [a] e funziona come previsto: restituisce una lista invertita (foldr (:) [] è essenzialmente id per le liste)

.

Tuttavia reverse . foldr (:) non digita controllo. Perché?

Quando tipi di corrispondenza per la composizione di funzione

Rivediamo alcuni tipi:

reverse      :: [a] -> [a]
foldr (:)    :: [a] -> [a] -> [a]
foldr (:) [] :: [a] -> [a]
(.)          :: (b -> c) -> (a -> b) -> a -> c

typechecks reverse . foldr (:) [], perché crea un'istanza (.) a:

(.) :: ([a] -> [a]) -> ([a] -> [a]) -> [a] -> [a]

In altre parole, nel tipo di annotazione per (.):

  • a diventa [a]
  • b diventa [a]
  • c diventa [a]

Quindi reverse . foldr (:) [] ha il tipo [a] -> [a].

Quando i tipi non corrispondono per la composizione di funzione

reverse . foldr (:) non digita controllo, però, perché:

foldr (:) :: [a] -> [a] -> [a]

Essendo il diritto operante di (.), sarebbe istanziare il tipo da a -> b a [a] -> ([a] -> [a]). Cioè, in:

(b -> c) -> (a -> b) -> a -> c
  • tipo di variabile a sarebbe sostituito con [a]
  • Inserisci b variabile sarebbe stato sostituito con [a] -> [a].

Se il tipo di foldr (:) era a -> b, il tipo di (. foldr (:)) sarebbe:

(b -> c) -> a -> c`

(foldr (:) viene applicato come operante diritto di (.)).

Ma perché il tipo di foldr (:) è [a] -> ([a] -> [a]), il tipo di (. foldr (:)) è:

(([a] -> [a]) -> c) -> [a] -> c

reverse . foldr (:) non digitare controllo, perché reverse ha il tipo [a] -> [a], non ([a] -> [a]) -> c!

Owl operatore

Quando la gente prima imparare la funzione di composizione in Haskell, vengono a sapere che quando si ha l'ultimo argomento della funzione in più a destra del corpo della funzione, si può cadere sia da argomenti e dal corpo, la sostituzione o la parentesi (o dollaro-segni) con i puntini. In altre parole, le sottostanti 4 definizioni di funzione sono equivalente :

f a x xs = g ( h a ( i x   xs))
f a x xs = g $ h a $ i x   xs
f a x xs = g . h a . i x $ xs
f a x    = g . h a . i x

Quindi, la gente ottiene un'intuizione che dice “ho appena rimuovere la variabile più a destra locale dal corpo e dagli argomenti”, ma questa intuizione è difettoso, perché una volta che è stato rimosso xs,

f a x = g . h a . i x
f a   = g . h a . i

non equivalente ! Si dovrebbe capire quando composizione typechecks funzione e quando non lo fa. Se quanto sopra 2 erano equivalenti, allora vorrebbe dire che il sotto 2 sono anche equivalenti:

f a x xs = g . h a . i x $ xs
f a x xs = g . h a . i $ x xs

che non ha senso, perché x non è una funzione con xs come parametro. x è un parametro alla funzione i, e xs è un parametro alla funzione (i x).

C'è un trucco per fare una funzione con 2 parametri punto-libera. E questo è quello di utilizzare un operatore “civetta”:

f a x xs = g . h a .  i x xs
f a      = g . h a .: i
  where (.:) = (.).(.)

Queste due definizioni di funzione sono equivalenti. Leggi più “civetta” operatore.

Bibliografia

programmazione Haskell diventa molto più facile e semplice, una volta capito le funzioni, i tipi di applicazioni, parziale e strigliare, composizione funzione e dollaro-operatore. Per inchiodare questi concetti, leggere le seguenti risposte StackOverflow:

Leggi anche:

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top