Dalam masalah pembelajaran yang telah saya lakukan, saya menyadari bahwa saya membutuhkan typeclass untuk fungsi dengan operasi untuk menerapkan, menyusun dll. Alasan ...
Akan lebih mudah untuk memperlakukan representasi suatu fungsi seolah-olah itu adalah fungsi itu sendiri, sehingga menerapkan fungsi tersebut secara implisit menggunakan penerjemah, dan menyusun fungsi memperoleh deskripsi baru.
Setelah Anda memiliki typeclass untuk fungsi, Anda dapat memiliki typeclasses untuk jenis fungsi khusus - dalam kasus saya, saya ingin fungsi yang dapat dibalik.
Misalnya, fungsi yang menerapkan offset bilangan bulat dapat diwakili oleh ADT yang berisi bilangan bulat. Menerapkan fungsi-fungsi itu hanya berarti menambahkan integer. Komposisi diimplementasikan dengan menambahkan bilangan bulat yang dibungkus. Fungsi terbalik memiliki bilangan bulat dinegasikan. Fungsi identitas membungkus nol. Fungsi konstan tidak dapat disediakan karena tidak ada representasi yang cocok untuk itu.
Tentu saja tidak perlu mengeja sesuatu seolah-olah nilainya adalah fungsi Haskell asli, tapi begitu saya punya ide, saya pikir perpustakaan seperti itu pasti sudah ada dan mungkin bahkan menggunakan ejaan standar. Tapi saya tidak dapat menemukan typeclass di perpustakaan Haskell.
Saya menemukan modul Data.Function , tetapi tidak ada typeclass - hanya beberapa fungsi umum yang juga tersedia dari Prelude.
Jadi - mengapa tidak ada typeclass untuk fungsi? Apakah itu "hanya karena tidak ada" atau "karena itu tidak berguna seperti yang Anda pikirkan"? Atau mungkin ada masalah mendasar dengan gagasan itu?
Masalah terbesar yang mungkin saya pikirkan sejauh ini adalah bahwa aplikasi fungsi pada fungsi aktual mungkin harus dibuat secara khusus oleh kompiler untuk menghindari masalah perulangan - untuk menerapkan fungsi ini saya perlu menerapkan fungsi aplikasi fungsi, dan untuk melakukan itu saya perlu memanggil fungsi aplikasi fungsi, dan untuk melakukan itu ...
Petunjuk lainnya
Kode contoh untuk menunjukkan apa yang saya tuju ...
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
-- In my first version, Doable only had the one argument f. This version
-- seemed to be needed to support the UndoableOffset type.
--
-- It seems to work, but it also seems strange. In particular,
-- the composition function - a and b are in the class, but c isn't,
-- yet there's nothing special about c compared with a and b.
class Doable f a b where
fwdApply :: f a b -> a -> b
compDoable :: f b c -> f a b -> f a c
-- In the first version, I only needed a constraint for
-- Doable f a b, but either version makes sense.
class (Doable f a b, Doable f b a) => Undoable f a b where
bwd :: f a b -> f b a
bwdApply :: f a b -> b -> a
bwdApply f b = fwdApply (bwd f) b
-- Original ADT - just making sure I could wrap a pair of functions
-- and there were no really daft mistakes.
data UndoableFn a b = UFN { getFwd :: a -> b, getBwd :: b -> a }
instance Doable UndoableFn a b where
fwdApply = getFwd
compDoable f g = UFN ((getFwd f) . (getFwd g)) ((getBwd g) . (getBwd f))
instance Undoable UndoableFn a b where
bwd f = UFN (getBwd f) (getFwd f)
bwdApply = getBwd
-- Making this one work led to all the extensions. This representation
-- can only represent certain functions. I seem to need the typeclass
-- arguments, but also to need to restrict which cases can happen, hence
-- the GADT. A GADT with only one constructor still seems odd. Perhaps
-- surprisingly, this type isn't just a toy (except that the whole thing's
-- a toy really) - it's one real case I need for the exercise. Still a
-- simple special case though.
data UndoableOffset a b where
UOFF :: Int -> UndoableOffset Int Int
instance Doable UndoableOffset Int Int where
fwdApply (UOFF x) y = y+x
compDoable (UOFF x) (UOFF y) = UOFF (x+y)
instance Undoable UndoableOffset Int Int where
bwdApply (UOFF x) y = y-x
bwd (UOFF x) = UOFF (-x)
-- Some value-constructing functions
-- (-x) isn't shorthand for subtraction - whoops.
undoableAdd :: Int -> UndoableFn Int Int
undoableAdd x = UFN (+x) (\y -> y-x)
undoableMul :: Int -> UndoableFn Int Int
undoableMul x = UFN (*x) (`div` x)
-- With UndoableFn, it's possible to define an invertible function
-- that isn't invertible - to break the laws. To prevent that, need
-- the UFN constructor to be private (and all public ops to preserve
-- the laws). undoableMul is already not always invertible.
validate :: Undoable f a b => Eq a => f a b -> a -> Bool
validate f x = (bwdApply f (fwdApply f x)) == x
-- Validating a multiply-by-zero invertible function shows the flaw
-- in the validate-function plan. Must try harder.
main = do putStrLn . show $ validate (undoableAdd 3) 5
putStrLn . show $ validate (undoableMul 3) 5
--putStrLn . show $ validate (undoableMul 0) 5
fb1 <- return $ UOFF 5
fb2 <- return $ UOFF 7
fb3 <- return $ compDoable fb1 fb2
putStrLn $ "fwdApply fb1 3 = " ++ (show $ fwdApply fb1 3)
putStrLn $ "bwdApply fb1 8 = " ++ (show $ bwdApply fb1 8)
putStrLn $ "fwdApply fb3 2 = " ++ (show $ fwdApply fb3 2)
putStrLn $ "bwdApply fb3 14 = " ++ (show $ bwdApply fb3 14)
Aplikasi ini melibatkan semacam penyatuan di mana nilai-nilai terpadu tidak sama, tetapi terkait melalui fungsi-fungsi yang tidak dapat dibalik - Logika gaya prolog tetapi dengan a = f(b)
kendala daripada a = b
. Sebagian besar komposisi akan dihasilkan dari pengoptimalan struktur penemuan-serikat. Kebutuhan akan invers harus jelas.
Jika tidak ada item dalam himpunan terpadu memiliki nilai yang tepat, maka item tertentu hanya dapat dikuantifikasi relatif terhadap item lain dalam himpunan terpadu tersebut. Itu sebabnya saya tidak ingin menggunakan fungsi "nyata" - menghitung nilai-nilai relatif itu. Saya dapat menghilangkan seluruh aspek fungsi dan hanya memiliki jumlah absolut dan relatif - Saya mungkin hanya perlu angka / vektor dan (+)
- tetapi astronot arsitektur batin saya menginginkan kesenangannya.
Satu-satunya cara saya memecah tautan lagi adalah melalui backtracking, dan semuanya murni - union-find akan dilakukan menggunakan kunci menjadi IntMap
"pointer". Saya memiliki fungsi union-find yang sederhana, tetapi karena saya belum menambahkan fungsi yang tidak dapat dibalik, tidak ada gunanya mencantumkannya di sini.
Alasan saya tidak bisa menggunakan Applicative, Monad, Arrow dll
Operasi utama yang saya butuhkan untuk kelas abstraksi fungsi adalah aplikasi dan komposisi. Itu terdengar akrab - misalnya Applicative
(<*>)
, Monad
(>>=)
dan Arrow
(>>>)
semua fungsi komposisi. Namun, tipe yang mengimplementasikan abstraksi fungsi dalam kasus saya akan berisi beberapa struktur data yang mewakili suatu fungsi, tetapi yang tidak (dan tidak dapat berisi) suatu fungsi, dan yang hanya dapat mewakili beberapa set fungsi yang terbatas.
Seperti yang saya sebutkan dalam penjelasan kode, kadang-kadang saya hanya dapat mengukur satu item relatif terhadap yang lain karena tidak ada item dalam "unified" cluster memiliki nilai yang tepat. Saya ingin dapat memperoleh representasi dari fungsi itu, yang secara umum akan menjadi komposisi dari beberapa fungsi yang disediakan (berjalan ke leluhur bersama di pohon penyatuan / menemukan) dan beberapa fungsi terbalik (berjalan kembali ke yang lain barang).
Kasus sederhana - di mana "fungsi" asli terbatas pada "fungsi" integer-offset, saya ingin hasil yang dikomposisikan sebagai "fungsi" integer-offset - tambahkan komponen offset. Itu adalah bagian besar dari mengapa fungsi komposisi perlu di kelas serta fungsi aplikasi.
Ini berarti saya tidak dapat menyediakan operasi pure
, return
atau arr
untuk tipe saya, jadi saya tidak dapat menggunakan Applicative
, Monad
atau Arrow
.
Ini bukan kegagalan tipe-tipe itu - ini adalah ketidakcocokan abstraksi. Abstraksi yang saya inginkan adalah fungsi murni sederhana. Tidak ada efek samping, misalnya, dan tidak perlu membuat notasi yang nyaman untuk mengurutkan dan menyusun fungsi selain yang setara dengan standar (.) Yang berlaku untuk semua fungsi.
Saya bisa contoh Category
. Saya yakin bahwa semua hal fungsional saya akan dapat memberikan identitas, meskipun saya mungkin tidak membutuhkannya. Tetapi karena Category
tidak mendukung aplikasi, saya masih perlu kelas turunan untuk menambahkan operasi itu.
Applicative
itu benar - itu membutuhkan nilai yang akan dibungkus serta fungsi, sedangkan saya hanya ingin membungkus fungsi, dan fungsi yang dibungkus benar-benar fungsi, sedangkan fungsi saya yang dibungkus biasanya tidak akan (dalam kasus yang paling umum, mereka AST yang menggambarkan fungsi). Di mana <*>
ada tipe f (a -> b) -> f a -> f b
, saya ingin operator aplikasi dengan tipe di g a b -> a -> b
mana a
dan b
tentukan domain dan codomain dari fungsi yang dibungkus, tetapi apa yang ada di dalam wrapper bukanlah (harus) fungsi yang sebenarnya. Di Arrows - mungkin, saya akan melihatnya.