Jak działa inferencja typu w obecności zależności funkcjonalnych
Rozważ poniższy kod:
{-# LANGUAGE MultiParamTypeClasses,FlexibleInstances,FunctionalDependencies,UndecidableInstances,FlexibleContexts #-}
class Foo a c | a -> c
instance Foo Int Float
f :: (Foo Int a) => Int -> a
f = undefined
Teraz, gdy widzę wywnioskowany typ f w ghci
> :t f
> f :: Int -> Float
Teraz, jeśli dodam następujący kod
g :: Int -> Float
g = undefined
h :: (Foo Int a) => Int -> a
h = g
Dostaję błąd
Could not deduce (a ~ Float)
Nie jestem w stanie zrozumieć, co tu się stało? OgraniczenieFoo Int a
powinien był ograniczyć typh
doInt -> Float
jak pokazano w wywnioskowanym typief
.
Czy to dlatego, że występuje unifikacja typów przed rozwiązaniem instancji?
[Aktualizacja]
Wyjaśnienie podane przez Dana Doela na liście dyskusyjnej kawiarni
Odpowiedź, jak sądzę, jest taka, że różnica między implementacją fundeple a rodzinami typu jest informacją o ograniczeniach lokalnych. Fundipy nie propagują lokalnego.
Tak więc w pierwszej definicji masz lokalnie dostarczoneInt -> a
„, który jest akceptowalny przez GHC. Następnie odkrywa zewnętrznie funkcję, którą „(Foo Int a) => Int -> a
' jest aktualneInt -> Float
.
W drugiej definicji próbujesz daćInt -> Float
„, ale GHC wie tylko lokalnie, że musisz dostarczyć”Int -> a
„z ograniczeniem”Foo Int a
„które toprzyzwyczajenie użyj, aby to ustalića ~ Float
.
Nie jest to związane z funduszami. Można stworzyć wersję funduszy, która ma lokalne reguły ograniczające (łatwo, tłumacząc na nowe typy rodzin). Ale różnica polega również na tym, że nakładające się instancje są obsługiwane dla funduszy, a nie dla rodzin typu. Ale teraz nie wejdę w to.
Nadal nie rozumiem, co to znaczy. Więc wciąż szukam lepszej zrozumiałej odpowiedzi.