Załóżmy, że lista L o długości n jest przeplatana na liście J o długości n + 1. Chcielibyśmy wiedzieć, dla każdego elementu J, który z sąsiadów z L jest większy. następujących funkcji zajmuje L jako wejście i tworzy listy k, również o długości n + 1, tak że I ty element K jest pożądane sąsiadem ı ty element J.Używanie systemu typów do sprawdzania długości wyjścia w zależności od listy wejściowej
aux [] prev acc = prev:acc
aux (hd:tl) prev acc = aux tl hd ((max hd prev):acc)
expand row = reverse (aux row 0 [])
mogę udowodnić sobie, nieoficjalnie, że długość wyniku tej funkcji (co ja pierwotnie napisał w SML) jest jednym większa niż długość danych wejściowych. Ale I przeniósł się do Haskell (nowy język dla mnie), ponieważ zainteresowałem się byciem zdolnym do udowodnienia poprzez system typów, który posiada ten niezmiennik. Z pomocą z this previous answer byłem stanie uzyskać w miarę następujące:
{-# LANGUAGE GADTs, TypeOperators, TypeFamilies #-}
data Z
data S n
type family (:+:) a b :: *
type instance (:+:) Z n = n
type instance (:+:) (S m) n = S (m :+: n)
-- A List of length 'n' holding values of type 'a'
data List a n where
Nil :: List a Z
Cons :: a -> List a m -> List a (S m)
aux :: List a n -> a -> List a m -> List a (n :+: (S m))
aux Nil prev acc = Cons prev acc
aux (Cons hd tl) prev acc = aux tl hd (Cons (max hd prev) acc)
Jednak ostatnia linia produkuje następujący błąd:
* Could not deduce: (m1 :+: S (S m)) ~ S (m1 :+: S m)
from the context: n ~ S m1
bound by a pattern with constructor:
Cons :: forall a m. a -> List a m -> List a (S m),
in an equation for `aux'
at pyramid.hs:23:6-15
Expected type: List a (n :+: S m)
Actual type: List a (m1 :+: S (S m))
* In the expression: aux tl hd (Cons (max hd prev) acc)
In an equation for `aux':
aux (Cons hd tl) prev acc = aux tl hd (Cons (max hd prev) acc)
* Relevant bindings include
acc :: List a m (bound at pyramid.hs:23:23)
tl :: List a m1 (bound at pyramid.hs:23:14)
aux :: List a n -> a -> List a m -> List a (n :+: S m)
(bound at pyramid.hs:22:1)
Wydaje się, że to, co trzeba do uczy kompilatora, że (x :+: (S y)) ~ S (x :+: y)
. czy to możliwe?
Czy istnieją lepsze narzędzia do tego problemu niż system typów?
Wygląda na to, że możliwe, ale wydaje się wymagać udowodnienia, że przez indukcję na 'x', i prawdopodobnie definiując i używając singleton dla' x' gdzieś. Ponadto, ta maszyna niestety nie zostanie usunięta w czasie wykonywania, więc będziemy marnować kroki O (n) tylko po to, aby wyprodukować pojedyncze "Refl". (Naprawdę chciałbym, aby GHC zoptymalizował to do O (1) pewnego dnia ... :)) – chi
@chi Wspomniałeś już kilka razy o tego rodzaju optymalizacji GHC (o czym pamiętam) i naprawdę zaczynam żałować, że to nie istnieje także. Czy są jakieś starania, aby to zrobić, o czym wiesz? – Alec
@Alec Przepraszam za przedłużony rant/życzenie, nic na to nie poradzę :) Nie śledzę rozwoju GHC, więc nie mogę komentować, czy są jakieś plany na to. – chi