Jste zde

Zajímavosti z Haskellu: Implicitní číselné typy

Dneska bych se chtěl mrknout na implicitní číselné typy v jazyce Haskell (podle specifikace z roku 1998, příklady odzkoušeny v GHC 6.10). Jak určitě víte, tak při použití funkcí jako je read :: Read a => String -> a je nutné specifikovat, co má být výsledným typem. Pokud zavoláte jenom read "143", tak překladač nedokáže automaticky odvodit konkrétní typ takového výrazu (netuší, že chcete z řetězce získat číslo typu Int). Řešením je typ výrazu explicitně zadat, čili read "143" :: Int. Tato nejednoznačnost se ale netýká jen takovýchto výrazů, ale i zdánlivě "jasných" výrazů.

Úvod do problému

Dejme tomu, že chcete vytvořit modul pro práci s vektory a v něm funkci, která vypočítá délku vektoru ve dvourozměrném prostoru. První, co vás napadne, je následující (uvažuji Euklidovský prostor):

-- Modul pro práci s 2D vektory v euklidovském prostoru
module Vector(Vector(..), vectorLength) where
 
-- Datový typ reprezentující 2D vektor
data Vector a = Vect a a deriving (Eq, Show)
 
-- Vypočítá délku předaného vektoru
vectorLength :: (Floating a) => Vector a -> a
vectorLength (Vect x y) = sqrt $ x^2 + y^2

Výše uvedený typ naší funkce vectorLength vyplývá z toho, že funkce sqrt, která provádí druhou odmocninu z předaného čísla, má typ sqrt :: (Floating a) => a -> a a operátor ^, který provádí umocnění, má typ (^) :: (Num a, Integral b) => a -> b -> a.

Potud nic zajímávého. Pokud se ale zamyslíte, tak v Haskellu existují standardně dva integrální typy - Int a Integer (liší se přesností - detaily viz Haskell Report 98, §6.4), čili celočíselné konstanty mohou být jak typu Int, tak typu Integer. Takže, má se číslo x umocňovat Intem nebo Integerem? V naší funkci to je nejednoznačné a abychom to napravili, tak bychom měli funkci přepsat takto:

vectorLength :: (Floating a) => Vector a -> a
vectorLength (Vect x y) = sqrt $ x^(2::Int) + y^(2::Int)

Musíte uznat, že to vypadá rozhodně méně čitelně, než předchozí verze a toto manuální typování všech číselných konstant začne po určité době nudit. Vám je ve skutečnosti jedno, zda se bude umocňovat Intem nebo Integerem. Co je zajímavé, tak když si vyzkoušíte naši původní implementaci, tak zjistíte, že se vše přeloží v pořádku a explicitní typování dvojkových konstant není nutné. Ale jakto? Co nejednoznačnost?

Řešení

Jako řešení jazyk Haskell nabízí tzv. deklaraci implicitních číselných typů (default numeric types declaration, Haskell Report 98, §4.3.4). V rámci modulu máme možnost specifikovat číselné typy, které se budou uvažovat při nejednoznačnostech, jako je nejednoznačnost typu číslených konstant v našem příkladě. Deklarace default má tvar          default (type1 , ... , typeN) (N>=0) a nás příklad by tedy vypadal takto:

-- Modul pro práci s 2D vektory v euklidovském prostoru
module Vector(Vector(..), vectorLength) where
 
default (Int) -- celočíselné konstanty budou implicitně typu Int
 
-- Datový typ reprezentující 2D vektor
data Vector a = Vect a a deriving (Eq, Show)
 
-- Vypočítá délku předaného vektoru
vectorLength :: (Floating a) => Vector a -> a
vectorLength (Vect x y) = sqrt $ x^2 + y^2

Jak je ale možné, že náš modul byl přeložitelný i bez této deklarace? Jazyk Haskell definuje pro každý modul implicitní default deklaraci default (Integer, Double), takže celočíselné konstanty jsou implicitně brány jako konstanty typu Integer a konstanty s pohyblivou řádovou čárkou jsou implicitně brány jako konstanty typu Double. Pokud bychom v našem modulu uvedli deklaraci default (), pak bychom skutečně museli explicitně uvést typ u obou dvojkových konstant.

Přidat komentář