Oto scenariusz: napisałem trochę kodu z podpisem typu, a skargi GHC nie mogły wydedukować x ~ y dla niektórych x
i y
. Zwykle możesz rzucić GHC kość i po prostu dodać izomorfizm do ograniczeń funkcji, ale jest to zły pomysł z kilku powodów:
- Nie podkreśla zrozumienia kodu.
- Możesz skończyć z 5 ograniczeniami, w których wystarczyłoby (na przykład, jeśli 5 jest implikowanych przez jedno bardziej szczegółowe ograniczenie)
- Możesz skończyć z fałszywymi ograniczeniami, jeśli zrobiłeś coś złego lub jeśli GHC jest nieprzydatny
Właśnie spędziłem kilka godzin walcząc ze sprawą 3. Bawię się syntactic-2.0
i próbowałem zdefiniować wersję niezależną od domeny share
, podobną do wersji zdefiniowanej w NanoFeldspar.hs
.
Miałem to:
{-# LANGUAGE GADTs, FlexibleContexts, TypeOperators #-}
import Data.Syntactic
-- Based on NanoFeldspar.hs
data Let a where
Let :: Let (a :-> (a -> b) :-> Full b)
share :: (Let :<: sup,
Domain a ~ sup,
Domain b ~ sup,
SyntacticN (a -> (a -> b) -> b) fi)
=> a -> (a -> b) -> a
share = sugarSym Let
i GHC could not deduce (Internal a) ~ (Internal b)
, co z pewnością nie było tym, do czego dążyłem. Więc albo napisałem kod, którego nie zamierzałem (co wymagało ograniczenia), albo GHC chciało tego ograniczenia z powodu innych ograniczeń, które napisałem.
Okazuje się, że musiałem dodać (Syntactic a, Syntactic b, Syntactic (a->b))
do listy ograniczeń, z których żaden nie sugeruje (Internal a) ~ (Internal b)
. Zasadniczo natknąłem się na właściwe ograniczenia; Nadal nie mam systematycznego sposobu na ich znalezienie.
Moje pytania to:
- Dlaczego GHC zaproponowało to ograniczenie? Nigdzie w składni nie ma ograniczenia
Internal a ~ Internal b
, więc skąd GHC to wzięło? - Zasadniczo, jakich technik można użyć do prześledzenia źródła ograniczenia, które według GHC jest potrzebne? Nawet dla ograniczeń, że można dowiedzieć się, moje podejście jest zasadniczo brute zmusza ścieżkę przestępstwa przez fizycznie spisując cyklicznych ograniczeń. To podejście polega zasadniczo na zejściu nieskończonej króliczej dziury ograniczeń i dotyczy mniej efektywnej metody, jaką mogę sobie wyobrazić.
a
i b
są powiązane - spójrz na podpis typu poza swoim kontekstem - a -> (a -> b) -> a
nie a -> (a -> b) -> b
. Może to jest to? Dzięki rozwiązaniom ograniczającym mogą one wpływać na równość przechodnią w dowolnym miejscu , ale błędy zwykle pokazują lokalizację „blisko” miejsca, w którym wywołano ograniczenie. Byłoby fajnie choć @jozefg - może oznaczać ograniczenia tagami lub czymś innym, aby pokazać, skąd się wzięły? : s