Linear Types
Po co?
- Zarządzanie zasobami:
- śledzenie alokacji pamięci
- operacje na plikach
- tania serializacja
- Modelowanie domeny:
- niektóre operacje powinny wykonać się tylko raz
- np. płatność za fakturę
TL;DR
Argument przed %1-> musi zostać użyty dokładnie raz.
linearId :: a %1-> a
linearId x = xwontTypecheck :: a %1-> (a, a)
wontTypecheck x = (x, x)Próba użycia argumentu dwukrotnie spowoduje błąd kompilacji.
wontTypecheckEither :: a %1-> Int
wontTypecheckEither _ = 42Nieużycie argumentu -- tak samo.
Sidebar
Gdyby zmienić:
Argument przed strzałką musi zostać użyty dokładnie raz
na:
Argument przed strzałką musi zostać użyty co najwyżej raz
dostajemy Affine Types, jak w Ruście.
Implikacje
Funkcja z liniowym argumentem może zrobić dwie rzeczy:
- zwrócić go
- przekazać do innej liniowej funkcji
printLinear :: String %1-> Linear.IO ()
printLinear s = print sNie zadziała! Zwykłe print nie jest liniowe.
Dlaczego tak jest?
Typechecker nie jest w stanie zagwarantować, że print użyje argumentu tylko raz.
Wytrych nr 1
Żeby móc zrobić cokolwiek, musimy używać liniowych wersji funkcji.
Dużo z nich jest w linear-base.
import Prelude.Linear
import qualified Control.Functor.Linear as Linear
import qualified System.IO.Linear as LIOTeraz możemy już skompilować coś takiego:
linearAdd :: Int %1-> Int %1-> Int
linearAdd = (+)Wytrych nr 2
To, że argument jest liniowy, nie oznacza, że jego pola muszą zostać użyte tylko raz:
nonLinearDup :: Ur a %1-> (a, a)
nonLinearDup (Ur x) = (x, x)Liniowe konstruktory
Nie tylko funkcje mogą być liniowe:
{#- LANGUAGE GADTs #-}
data LinearWrapper a where
LinearWrapper :: a %1-> LinearWrapper aProblemy
let, where i case konsumują swój argument więcej, niż raz (według typecheckera...)
wontCompile :: a %1-> a
wontCompile x = let y = x in yPattern matching wymaga sporej gimnastyki...
InvoiceDraft :: Ur String %1-> Ur Int %1-> Invoice DraftTechnikalia
Linear types dostępne są jako rozszerzenie Haskella od GHC 9.
{-# LANGUAGE LinearTypes #-}Liniowe wersje popularnych funkcji znajdują się w linear-base.
Bardzo wygodnie jest używać również jednego z nowych rozszerzeń:
{#- LANGUAGE QualifiedDo #-}Które pozwala nam używać liniowych wersji operatorów
z do-notation:
import qualified Control.Monad.Linear as Linear
import qualified System.IO.Linear as LIO
test :: a %-> a%-> LIO.IO ()
test x y = Linear.do
process x
process y
Linear Types
By ambrozyk
Linear Types
- 209