Etrafta dolaştığım bir öğrenme probleminde, uygulama, besteleme vb. İşlemleri için bir tip sınıfına ihtiyacım olduğunu fark ettim.
Bir işlevin temsilini, sanki işlevin kendisiymiş gibi ele almak uygun olabilir, böylece işlevin dolaylı olarak uygulanması bir yorumlayıcı kullanır ve işlevler oluşturmak yeni bir açıklama oluşturur.
Fonksiyonlar için bir tip sınıfınız olduğunda, özel fonksiyon türleri için türetilmiş sınıflar olabilir - benim durumumda, ters çevrilebilir fonksiyonlar istiyorum.
Örneğin, tamsayı ofsetleri uygulayan fonksiyonlar bir tamsayı içeren bir ADT ile temsil edilebilir. Bu işlevleri uygulamak, tamsayı eklemek anlamına gelir. Kompozisyon, sarılmış tamsayılar eklenerek uygulanır. Ters fonksiyonun tamsayısı reddedilir. Kimlik işlevi sıfırı sarar. Sabit işlev sağlanamaz çünkü onun için uygun bir temsil yoktur.
Elbette, değerleri gerçek Haskell işlevleriymiş gibi hecelemeye gerek yok, ama bir kez fikre sahip olduğumda, böyle bir kütüphanenin zaten var olması ve hatta standart yazımları kullanması gerektiğini düşündüm. Ama Haskell kütüphanesinde böyle bir sınıf bulamıyorum.
Data.Function modülünü buldum , ancak tip sınıfı yok - sadece Prelude'da bulunan bazı ortak işlevler.
Öyleyse - neden fonksiyonlar için bir sınıf sınıfı yok? "Sadece olmadığı için" veya "düşündüğünüz kadar yararlı olmadığı için" mi? Ya da belki fikirle ilgili temel bir sorun var mı?
Şimdiye kadar düşündüğüm en büyük sorun, gerçek işlevler üzerindeki işlev uygulamasının, bir döngü sorununu önlemek için derleyici tarafından özel olarak kastedilmesi gerektiğidir - bu işlevi uygulamak için işlev uygulama işlevini uygulamam gerekir, ve bunu yapmak için işlev uygulama işlevini çağırmak ve bunu yapmak gerekir ...
Daha Fazla İpucu
Neyi hedeflediğimi göstermek için örnek kod ...
{-# 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)
Uygulama, birleştirilmiş değerlerin eşit olmadığı, ancak bu ters çevrilebilir işlevler - Prolog tarzı mantıkla değil, a = f(b)
kısıtlamalarla ilişkili olduğu bir tür birleştirmeyi içerir a = b
. Kompozisyonun çoğu sendika bulma yapısının optimize edilmesinden kaynaklanacaktır. Tersine çevirme ihtiyacı açık olmalıdır.
Birleştirilmiş bir kümedeki hiçbir öğenin kesin bir değeri yoksa, belirli bir öğe yalnızca o birleştirilmiş kümedeki başka bir öğeye göre ölçülebilir. Bu yüzden "gerçek" işlevleri kullanmak istemiyorum - bu göreceli değerleri hesaplamak. Tüm fonksiyon yönünü düşürebilirim ve sadece mutlak ve göreceli miktarlara sahip olabilirim - muhtemelen sadece sayılara / vektörlere ihtiyacım var ve (+)
- ama iç mimarım astronot eğlencesini istiyor.
Bağlantıları tekrar parçalara ayırmanın tek yolu geri izlemedir ve her şey saftır - sendika bulma anahtarları kullanarak bir IntMap
"işaretçiler" olarak yapılacaktır. Basit sendika bulma çalışması var, ancak henüz ters çevrilebilir işlevleri eklemediğim için, burada listelemenin bir anlamı yok.
Uygulayıcı, Monad, Ok vb.
Sağlamak için fonksiyon soyutlama sınıfına ihtiyacım olan ana işlemler uygulama ve kompozisyon. Kulağa tanıdık geliyor - örneğin Applicative
(<*>)
, Monad
(>>=)
ve Arrow
(>>>)
hepsi kompozisyon işlevleridir. Ancak benim durumumda işlev soyutlamasını uygulayan türler, bir işlevi temsil eden, ancak bir işlevi olmayan (ve içeremeyen) ve yalnızca bazı sınırlı işlev kümesini temsil edebilen bazı veri yapısı içerecektir.
Kodun açıklamasında belirtildiği gibi, bazen "birleştirilmiş" kümedeki hiçbir öğenin kesin bir değeri olmadığından, yalnızca bir öğeyi diğerine göre ölçebilirim. Genel olarak, sağlanan bazı fonksiyonların (birlik / ağaçta ortak bir ataya kadar yürürken) ve birkaç ters fonksiyonun (diğerine geri yürüme) bileşimi olacak bu fonksiyonun bir temsilini türetmek istiyorum. öğesi).
Basit durum - orijinal "fonksiyonlar" tamsayı-ofset "fonksiyonlar" ile sınırlı olduğunda, ben oluşturulan sonucu bir tamsayı-ofset "fonksiyonu" olarak istiyorum - bileşen ofsetleri ekleyin. Bu, kompozisyon işlevinin neden uygulama işlevinin yanı sıra sınıfta olması gerektiğinin büyük bir parçasıdır.
Bu araçlar Ben işlemlerini sağlayamaz pure
, return
ya arr
benim türleri için, ben kullanamaması için Applicative
, Monad
ya da Arrow
.
Bu, bu tür bir başarısızlık değil - soyutlamaların uyumsuzluğu. İstediğim soyutlama basit saf bir işleve sahip. Örneğin herhangi bir yan etki yoktur ve tüm işlevler için geçerli olan standardın (.) Eşdeğeri dışında işlevlerin sıralanması ve oluşturulması için uygun bir gösterim oluşturmaya gerek yoktur.
Ben örnek verebilirimCategory
. Tüm işlevsel şeylerimin bir kimlik sağlayabileceğinden eminim, ancak muhtemelen buna ihtiyacım yok. Ancak Category
uygulamayı desteklemediğinden, yine de bu işlemi eklemek için türetilmiş bir sınıfa ihtiyacım var.
Applicative
Oldukça doğru olduğunu düşünmüyorum - değerlerin işlevlerin yanı sıra sarılmasını gerektirir, ancak yalnızca işlevleri sarmak istiyorum ve sarılmış işlevler gerçekten işlevler, oysa sarılmış işlevlerim normalde (içinde en genel durum, işlevleri tanımlayan AST'lerdir). Nerede <*>
türü vardır f (a -> b) -> f a -> f b
, ben türüyle bir uygulama operatörü istiyorum ve etki alanını belirlemek ve sarılı fonksiyonun değer kümesi, ama ne mahfaza içine değil (mutlaka) gerçek işlevdir. Oklarda - muhtemelen, bir göz atacağım. g a b -> a -> b
a
b