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.
sumber
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 tipef (a -> b) -> f a -> f b
, saya ingin operator aplikasi dengan tipe dig a b -> a -> b
manaa
danb
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.Jawaban:
Ya, saya tidak tahu ada ide-ide yang memasarkan diri mereka sebagai mewakili hal-hal "fungsi-y". Tetapi ada beberapa yang mendekati
Kategori
Jika Anda memiliki konsep fungsi sederhana yang memiliki identitas dan komposisi, daripada Anda memiliki kategori.
Kelemahannya adalah bahwa Anda tidak dapat membuat kategori contoh bagus dengan satu set objek (
a
,b
, danc
). Anda dapat membuat kelas kategori kustom yang saya kira.Panah
Jika fungsi Anda memiliki gagasan tentang produk dan dapat menyuntikkan fungsi sewenang-wenang, daripada panah untuk Anda
ArrowApply
memiliki gagasan tentang aplikasi yang terlihat penting untuk apa yang Anda inginkan.Pelamar
Pelamar memiliki gagasan aplikasi Anda, saya telah menggunakannya dalam AST untuk mewakili aplikasi fungsi.
Ada banyak ide lain. Tetapi tema yang umum adalah untuk membangun beberapa struktur data yang mewakili fungsi Anda, dan kemudian meneruskannya ke fungsi interpretasi.
Ini juga berapa banyak monad gratis yang berfungsi. Saya sarankan menyodok ini jika Anda merasa berani, mereka adalah alat yang ampuh untuk hal-hal yang Anda sarankan dan pada dasarnya membiarkan Anda membangun struktur data menggunakan
do
notasi dan kemudian runtuh menjadi sisi yang mempengaruhi perhitungan dengan fungsi yang berbeda . Tetapi keindahannya adalah bahwa fungsi-fungsi ini hanya beroperasi pada struktur data, dan tidak benar-benar menyadari bagaimana Anda membuat semuanya. Inilah yang akan saya sarankan untuk contoh Anda seorang penerjemah.sumber
($)
. Panah terlihat seperti kerja keras yang sangat besar pada pandangan pertama, tetapi masihArrowApply
terdengar menjanjikan - selama saya tidak perlu memberikan apa pun yang saya tidak bisa, mungkin tidak masalah. +1 untuk saat ini, dengan lebih banyak pemeriksaan yang harus dilakukan.Applicative
atauArrow
(atauMonad
) - Saya tidak dapat membungkus fungsi normal (secara umum) karena nilai tipe saya mewakili fungsi tetapi diwakili oleh data, dan tidak akan mendukung fungsi sewenang-wenang jika jika ada cara untuk menerjemahkan. Itu berarti saya tidak bisa menyediakanpure
,arr
ataureturn
untuk contoh. BTW - kelas-kelas itu berguna tetapi saya tidak bisa menggunakannya untuk tujuan khusus ini.Arrow
bukan "pembunuhan besar-besaran" - itu adalah kesan yang salah dari terakhir kali saya mencoba membaca koran, ketika saya tidak siap untuk memahaminya.Seperti yang Anda tunjukkan, masalah utama dengan menggunakan Aplikasi di sini adalah tidak ada definisi yang masuk akal untuk
pure
. Oleh karena itu,Apply
diciptakan. Setidaknya, itulah pemahaman saya tentang hal itu.Sayangnya, saya tidak punya contoh di tangan contoh
Apply
yang tidak jugaApplicative
. Dikatakan ini memang benarIntMap
, tetapi saya tidak tahu mengapa. Demikian pula, saya tidak tahu apakah contoh Anda - offset bilangan bulat - menerimaApply
contoh.sumber
Selain disebutkan
Category
,Arrow
, danApplicative
:Saya juga menemukan
Data.Lambda
oleh Conal Elliott:Terlihat menarik, tentu saja, tetapi sulit dimengerti tanpa contoh ...
Contohnya
Contohnya dapat ditemukan di halaman wiki tentang nilai nyata (TV) yang tampaknya menjadi salah satu hal yang menyebabkan penciptaan
TypeCompose
perpustakaan; lihat Input dan output bernilai fungsi .Gagasan perpustakaan TV adalah untuk menampilkan nilai Haskell (termasuk fungsi) secara nyata.
Untuk mengikuti aturan StackOverflow tentang tidak memposting bon lonks, saya menyalin beberapa bit di bawah ini yang seharusnya memberikan ide tentang hal-hal ini:
Contoh pertama berbunyi:
yang memberi ketika dijalankan sebagai
runIO shopping
(lihat di sana untuk lebih banyak komentar, GUI, dan lebih banyak contoh):sumber
Data.Lambda
memberikan kelas untuk hal-hal seperti fungsi (yang diminta) ... Saya tidak yakin bagaimana hal-hal ini digunakan. Saya sedikit mengeksplorasi ini. Mungkin, mereka tidak menyediakan abstraksi untuk aplikasi fungsi.