Bir arabirime işlevsel programlama alternatifi nedir?


15

"İşlevsel" bir tarzda programlamak istersem, bir arayüzle ne değiştirirdim?

interface IFace
{
   string Name { get; set; }
   int Id { get; }
}
class Foo : IFace { ... }

Belki bir Tuple<>?

Tuple<Func<string> /*get_Name*/, Action<String> /*set_Name*/, Func<int> /*get_Id*/> Foo;

İlk etapta bir arayüz kullanmamın tek nedeni, her zaman belirli özellikleri / yöntemleri kullanılabilir olmasını istememdir.


Düzenleme: Ne düşündüğüm / denediğim hakkında biraz daha ayrıntı.

Diyelim ki üç işlevi yerine getiren bir yöntemim var:

static class Blarf
{
   public static void DoSomething(Func<string> getName, Action<string> setName, Func<int> getId);
}

Bir örneği ile Barbu yöntemi kullanabilirsiniz:

class Bar
{
   public string GetName();
   public void SetName(string value);

   public int GetId();
}
...
var bar = new Bar();
Blarf.DoSomething(bar.GetName, bar.SetName, bar.GetId);

Ama bartek bir çağrıda üç kez bahsetmem gerektiğinden bu biraz acı verici . Ayrıca, arayanların farklı örneklerden işlevler sağlaması niyetinde değilim

Blarf.DoSomething(bar1.GetName, bar2.SetName, bar3.GetId); // NO!

C # 'da, interfacebununla başa çıkmanın bir yoludur; ama bu çok nesne yönelimli bir yaklaşım gibi görünüyor. Daha işlevsel bir çözüm olup olmadığını merak ediyorum: 1) fonksiyon grubunu birlikte geçirin ve 2) fonksiyonların birbirleriyle düzgün bir şekilde ilişkili olduğundan emin olun.


Yapmazsın. Veri türleri için arayüzler gayet iyi (değişmez nesneleri tercih etmenize rağmen).
Telastyn

1
SICP'in 2. bölümü bununla ilgili.
user16764

7
Sorunuzu yeniden okuduktan sonra, hangi özel işlevi yerine getirmeye çalıştığınızı merak ediyorum? Sorduğunuz gibi, mantıklı olmayan fonksiyonel bir tarzda bir örneğe karşı oo tarzı yan etkili programmin nasıl yapılacağıdır ..
Jimmy Hoffa

Cevap dile bağlı olacaktır. Clojure'da clojure.org/protocols kullanabilirsiniz , burada tek yumuşak alan işlevlerin üzerinde çalışması gereken parametre türleridir - bunlar bir nesnedir - tek bildiğiniz şey budur.
İş

1
Basitleştirin: bu yöntem işaretleyicilerini içeren bir yapı ve bunu bir nesne örneğinden başlatmak için bir işlev. Neden Haskell? ;)
mlvljr

Yanıtlar:


6

İşlevsel programlamayı zorunlu programlamaya göre ince bir kaplama olarak ele almayın; sözdizimsel bir farktan çok daha fazlası var.

Bu durumda, GetIDnesnelerin benzersizliğini ima eden bir yönteminiz vardır. Bu fonksiyonel programlar yazmak için iyi bir yaklaşım değildir. Belki de çözmeye çalıştığınız sorunu bize söyleyebilir ve size daha anlamlı tavsiyeler verebiliriz.


3
" Rusça düşünmelisin ."
Mart'ta

Yeterince adil; asıl sorunum özellikle ilginç değil. Zaten C # ( github.com/JDanielSmith/Projects/tree/master/PictureOfTheDay gerçekten koda bakmak istiyorsanız) iyi şeyler var zaten , ama daha fonksiyonel bir tarzda yapmak eğlenceli olurdu hala C # kullanıyor.
13'te

1
Ðаn @ Bu gerçekten mi Firefox (film) tırnak (yani çünkü müthiş bunun ise)? Yoksa başka bir yerde mi kullanılıyor?
icc97

2
Kabul ettiğim gibi, Emperitif'ten Fonksiyonel Programlamaya tam bir paradigma kayması, imperitif bir şekilde yazılmış çok sayıda daha fazla kod satırı vardır. Böylece, devasa sistemler için yazılan köşe vakalarının çok daha fazlası, imperitif programlama ile bulunmuştur. Belirsiz programlama ve bu becerilerin çevrilip çevrilemeyeceğini veya FP'de bir sorun olup olmadıklarını bilmek iyi bir sorudur. FP'de korkunç kod yazmak tamamen mümkündür, bu nedenle bu tür sorular FP'nin iyi kısımlarını da vurgulamalıdır.
icc97

11

Haskell ve türevleri arayüzlere benzer tip sınıflara sahiptir. Kapsülleme nasıl yapılacağını soruyor gibi görünse de, bu tür sistemler ile ilgili bir soru. Hindley Milner tipi sistem fonksiyonel dillerde yaygındır ve bunu sizin için diller arasında değişen bir şekilde yapan veri türlerine sahiptir.


5
Typeclasses için +1 - Haskell tip sınıfı ve Java arabirimi arasındaki temel fark, tipeclass'ın her ikisi de ayrı olarak bildirildikten sonra türle ilişkilendirilmesidir . Eski bir türü yeni bir "arabirim" aracılığıyla, yeni bir türe erişmek için eski bir "arabirimi" kullanabildiğiniz kadar kolayca kullanabilirsiniz. Veri gizleme için, bir modülde türün uygulanmasını gizlersiniz. Göre en az Eiffel ün Bertrand Meyer , bir OOP sınıfı olan modül bir tür.
Steve314

5

Bir işlevin birden çok girişle baş etmesine izin vermenin birkaç yolu vardır.

İlk ve en yaygın: Parametrik Polimorfizm.

Bu, bir işlevin rasgele türler üzerinde çalışmasını sağlar:

--Haskell Example
id :: a -> a --Here 'a' is just some arbitrary type
id myRandomThing = myRandomThing

head :: [a] -> a
head (listItem:list) = listItem

Bu güzel, ama size OO arabirimlerinin sahip olduğu dinamik gönderimi vermiyor. Bunun için Haskell'in karakteristikleri vardır, Scala'nın etkileri vardır, vb.

class Addable a where
   (<+>) :: a -> a -> a
instance Addable Int where
   a <+> b = a + b
instance Addable [a] where
   a <+> b = a ++ b

--Now we can get that do something similar to OO (kinda...)
addStuff :: (Addable a) => [a] -> a
-- Notice how we limit 'a' here to be something Addable
addStuff (x:[]) = x
addStuff (x:xs) = x <+> addStuff xs
-- In better Haskell form
addStuff' = foldl1 <+>

Bu iki mekanizma arasında, türlerinizle ilgili her türlü karmaşık ve ilginç davranışı ifade edebilirsiniz.


1
Yanıttaki dil sorudaki dil ile eşleşmediğinde sintax vurgulama ipuçları ekleyebilirsiniz . Örneğin, önerilen düzenlememe bakın.
13'te sarılmak

1

Temel kural, FP programlama fonksiyonlarında OO-programlamada nesnelerle aynı işi yapmaktır. Yöntemlerini çağırabilirsiniz (yine de "call" yöntemi) ve bazı kapsüllenmiş iç kurallara göre yanıt verirler. Özellikle, her nezih FP dili, fonksiyonlarınızda kapanış / sözcüksel kapsam belirleme ile "örnek değişkenleri" olmasını sağlar.

var make_OO_style_counter = function(){
   return {
      counter: 0
      increment: function(){
          this.counter += 1
          return this.counter;
      }
   }
};

var make_FP_style_counter = function(){
    var counter = 0;
    return fucntion(){
        counter += 1
        return counter;
    }
};

Şimdi bir sonraki soru bir arayüzle ne demek istiyorsun? Bir yaklaşım nominal arayüzler kullanmaktır (bunu söylüyorsa arayüze uygundur) - bu genellikle hangi dili kullandığınıza çok bağlıdır, bu yüzden ikinci dil için bırakın. Bir arayüz tanımlamanın diğer yolu, bir şeyin hangi parametreleri aldığını ve geri döndüğünü görerek yapısal yöntemdir. Bu, dinamik, ördek tipi dillerde görmeye meyilli olduğunuz bir arayüz türüdür ve tüm FP'lere çok uygundur: bir arayüz sadece fonksiyonlarımıza giriş parametrelerinin türleri ve geri döndükleri türlerdir, böylece tüm fonksiyonlar doğru tipler arayüze uygun!

Bu nedenle, bir arabirimle eşleşen bir nesneyi temsil etmenin en basit yolu, basitçe bir grup işleve sahip olmaktır. Genellikle işlevleri bir tür kayıtta paketleyerek ayrı ayrı geçirmenin çirkinliğini giderirsiniz:

var my_blarfable = {
 get_name: function(){ ... },
 set_name: function(){ ... },
 get_id:   function(){ ... }
}

do_something(my_blarfable)

Çıplak işlevlerin veya işlev kayıtlarının kullanılması, tonlarca kazan plakası olmadan yaygın sorunlarınızın çoğunu "yağsız" bir şekilde çözmede uzun bir yol kat edecektir. Bundan daha gelişmiş bir şeye ihtiyacınız varsa, bazen diller size ekstra özellikler sağlar. İnsanların bahsettiği örneklerden biri Haskell tipi sınıflardır. Tür sınıfları, bir türü temel olarak bu işlev kayıtlarından biriyle ilişkilendirir ve sözlükler örtük olması ve iç işlevlere uygun şekilde otomatik olarak aktarılması için bir şeyler yazmanıza olanak tanır.

-- Explicit dictionary version
-- no setters because haskell doesn't like mutable state.
data BlargDict = BlargDict {
    blarg_name :: String,
    blarg_id   :: Integer
}

do_something :: BlargDict -> IO()
do_something blarg_dict = do
   print (blarg_name blarg_dict)
   print (blarg_id   blarg_dict)

-- Typeclass version   
class Blargable a where
   blag_name :: a -> String
   blag_id   :: a -> String

do_something :: Blargable a => a -> IO
do_something blarg = do
   print (blarg_name blarg)
   print (blarg_id   blarg)

Bununla birlikte, tipler hakkında not edilmesi gereken önemli bir nokta, sözlüklerin değerlerle değil türlerle ilişkili olmasıdır (sözlükte ve OO sürümlerinde olduğu gibi). Bu, yazı sisteminin "türleri" karıştırmanıza izin vermediği anlamına gelir [1]. Bir "blargables" listesi veya blargables alan bir ikili fonksiyon istiyorsanız, sözlük yaklaşımı farklı türlerden blargable'lara sahip olmanıza izin verirken, tipler her şeyin aynı tipte olmasını sağlar (hangi sürüm daha iyi olduğunuza çok bağlıdır. ) yapıyor

[1] "Varoluşçu Tipler" i yapmanın gelişmiş yolları vardır, ancak genellikle sorun yaratmaya değmez.


0

Bence bu dile özgü olacak. Lispy kökenliim. Çoğu durumda, durum ile olan arayüzler fonksiyonel modeli bir dereceye kadar kırmaktadır. Örneğin CLOS, LISP'nin daha az işlevsel ve zorunlu bir dile daha yakın olduğu yerdir. Genel olarak, gerekli işlev parametreleri daha üst düzey yöntemlerle birleştirildiğinde muhtemelen aradığınız şeydir.

;; returns a function of the type #'(lambda (x y z &optional a b c)

(defun get-higher-level-method-impl (some-type-of-qualifier) 
    (cond ((eq 'foo) #'the-foo-version)
          ((eq 'bar) #'the-bar-version)))
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.