Cebirsel veri türleri hangi problemi çözüyor?


18

Adil uyarı, fonksiyonel programlamada yeniyim, bu yüzden birçok kötü varsayım taşıyabilirim.

Cebirsel türleri öğreniyorum. Birçok işlevsel dilde bunlara sahip gibi görünmektedir ve desen eşleşmesi ile birlikte oldukça yararlıdırlar. Ancak, gerçekte hangi problemi çözüyorlar? C # gibi bir görünüşte (sıralama) cebirsel türü uygulayabilirsiniz:

public abstract class Option { }
public class None : Option { }
public class Some<T> : Option
{
    public T Value { get; set; }
}

var result = GetSomeValue();
if(result is None)
{
}
else
{
}

Ama bence çoğu, bunun nesne yönelimli programlamanın piç haline getirilmesi olduğunu kabul ediyor ve bunu asla yapmamalısınız. İşlevsel programlama sadece bu tarz bir programlamanın daha az kaba görünmesini sağlayan daha temiz bir sözdizimi ekliyor mu? Başka ne eksik?


6
Fonksiyonel programlama, nesne yönelimli programlamadan farklı bir paradigmadır.
Basile Starynkevitch

@BasileStarynkevitch Anlıyorum, ancak F # gibi her ikisinden de biraz farklı diller var. Soru fonksiyonel dillerle ilgili değil, cebirsel veri türlerinin hangi problemi çözdüğü hakkında daha fazla.
ConditionRacer

7
Ben tanımlamak ne olur class ThirdOption : Option{}ve size vermek new ThirdOption()beklediğiniz yerde Someya None?
amon

1
Toplam türlerine sahip @ amon dillerinde genellikle izin vermeme yöntemleri vardır. Örneğin, Haskell tanımlar data Maybe a = Just a | Nothing( örneğinize eşdeğerdir data Option a = Some a | None): post-hoc üçüncü bir vaka ekleyemezsiniz. Gösterdiğiniz şekilde C #'daki toplam türlerini öykünebilirsiniz, ancak bu en güzel değildir.
Martijn

1
Bence daha az "ADT'ler hangi sorunu çözüyor" ve daha çok "ADT'ler olaylara yaklaşmanın farklı bir yoludur".
MathematicalOrchid

Yanıtlar:


44

Arabirimi ve kalıtımı olan sınıflar açık bir dünya sunar: Herkes yeni bir tür veri ekleyebilir. Belirli bir arayüz için, tüm dünyada, farklı dosyalarda, farklı projelerde, farklı şirketlerde uygulayan sınıflar olabilir. Veri yapılarına vaka eklemeyi kolaylaştırırlar, ancak arayüzün uygulamaları merkezi olmayan olduğundan, arayüze yeni bir yöntem eklemek zordur. Bir arayüz herkese açık olduğunda, temel olarak dondurulur. Hiç kimse olası tüm uygulamaları bilmiyor.

Cebirsel veri türleri bunun ikisidir, kapalıdır . Tüm veri vakaları tek bir yerde listelenir ve işlemler sadece varyantları kapsamlı bir şekilde listelemekle kalmaz , aynı zamanda bunu yapmaya teşvik edilir. Sonuç olarak bir cebirsel veri türü üzerinde çalışan yeni bir fonksiyon yazmak önemsizdir: Sadece lanet olası fonksiyonu yazınız. Buna karşılık, temelde tüm kod tabanından geçmeniz ve her birini genişletmeniz gerektiğinden, yeni vakalar eklemek karmaşıktır match. Arabirimlerdeki duruma benzer şekilde, Rust standart kitaplığında yeni bir varyant eklemek (genel türler için) bir değişikliktir .

Bunlar ifade sorununun iki yüzüdür . Cebirsel veri türleri onlar için eksik bir çözümdür, ama OOP de öyle. Her ikisinin de kaç veri vakası olduğuna, bu vakaların ne sıklıkta değiştiğine ve işlemlerin ne sıklıkta genişletildiğine veya değiştirildiğine bağlı olarak avantajları vardır. (Bu yüzden birçok modern dil her ikisini de benzer bir şey sağlar veya her iki yaklaşımı da kullanmaya çalışan daha güçlü ve daha karmaşık mekanizmalar için doğrudan gider.)


Bir eşleşmeyi güncellemezseniz derleyici hatası mı alıyorsunuz, yoksa yalnızca çalışma zamanında öğrendiniz mi?
Ian

6
@Ian İşlevsel dillerin çoğu statik olarak yazılmıştır ve örüntü eşleştirmenin tamlığını kontrol eder. Ancak, bir "tümünü yakala" kalıbı varsa, işlev işini yapmak için yeni durumla uğraşmak zorunda kalsa bile derleyici mutlu olur. Ayrıca, tüm bağımlı kodları yeniden derlemeniz gerekir, yalnızca bir kitaplığı derleyemez ve önceden oluşturulmuş bir uygulamaya yeniden bağlayamazsınız.

Ayrıca, bir paternin kapsamlı olup olmadığını statik olarak kontrol etmek genel olarak pahalıdır. SAT problemini en iyi şekilde çözer ve en kötü ihtimalle dil keyfi tahminlere izin verir, bu da bunu kararsız kılar.
usr

@usr Hiçbir dil, mükemmel bir çözüm denediğinin farkında değilim, derleyici bunun kapsamlı olduğunu anlıyor veya çöktüğünüz ve yaktığınız bir tümünü yakalama vakası eklemek zorundasınız. SAT ile bir ilişkim olduğunu bilmiyorum, bir indirgeme bağlantınız var mı? Ne olursa olsun, gerçek programlarda yazılan gerçek kod için, kapsamlılık denetimi grupta bir düşüştür.

N boolean ile eşleştiğinizi düşünün. Sonra (a, b, _, d, ...) gibi eşleşme cümleleri eklersiniz. Hepsini yakalama vakası! Clause1 &&! Clause2 && .... Bana SAT gibi geliyor.
usr

12

İşlevsel programlama sadece bu tarz bir programlamanın daha az kaba görünmesini sağlayan daha temiz bir sözdizimi ekliyor mu?

Bu belki de bir basitleştirme, ama evet.

Başka ne eksik?

Cebirsel veri türlerinin ne olduğu konusunda net olalım ( Haskell olarak Öğrenin'deki bu ince bağlantıyı özetler ):

  • "Bu değer A veya B olabilir" yazan bir toplam türü .
  • Bir ürün tipi "Bu değer bir A hem söylüyor ve bir B".

örneğin sadece gerçekten ilkiyle çalışır.

Belki de eksik olan şey, bu iki temel işlemi sağlayarak, işlevsel dillerin her şeyi oluşturmanıza izin vermesidir. C # 'da yapıların, sınıfların, numaraların, jeneriklerin ve bunların nasıl davranacağını yönetecek kurallar vardır.

Yardım etmek için bazı sözdizimi ile birlikte, işlevsel diller bu iki yola kadar işlemleri bozabilir ve tiplere temiz, basit ve zarif bir yaklaşım sağlar.

Cebirsel veri türleri hangi problemi çözüyor?

Diğer herhangi bir sistemle aynı sorunu çözüyorlar: "burada hangi değerler yasaldır?" - sadece farklı bir yaklaşım sergilerler.


4
Son cümleniz, birine "gemiler uçaklarla aynı sorunu çözdüğünü söylemek gibi: ulaşım - sadece farklı bir yaklaşım benimsiyorlar". Bu tamamen doğru bir ifade ve aynı zamanda oldukça işe yaramaz bir ifade.
user541686

@mehrdad - Bence bu biraz abartıyor.
Telastyn

1
Şüphesiz cebirsel veri türlerini iyi anlıyorsunuz, ancak madde işaretli listeniz ("Bir toplam türü ..." ve "Bir ürün türü ...") daha çok birleşme ve kavşak türlerinin bir tanımına benziyor. toplam ve ürün türleri ile aynı şey.
pyon

6

Desen eşleştirmenin Seçenekler ile çalışmanın en deyimsel yolu olarak görülmediğini öğrenmek sizi şaşırtabilir. Bununla ilgili daha fazla bilgi için Scala Seçeneklerinin belgelerine bakın . Pek çok FP öğreticisinin bu kullanımı neden teşvik ettiğinden emin değilim.

Çoğunlukla eksik olan, Seçeneklerle çalışmayı kolaylaştırmak için oluşturulmuş bir sürü işlev var. Scala belgelerindeki ana örneği düşünün:

val name: Option[String] = request getParameter "name"
val upper = name map { _.trim } filter { _.length != 0 } map { _.toUpperCase }
println(upper getOrElse "")

Nasıl Bildirim mapve filtersen olup olmadığını her noktada kontrol etmek zorunda kalmadan, Seçenekler işlemleri zincirle izin Noneya da değil. Ardından, getOrElsevarsayılan bir değer belirtmek için kullanın . Hiçbir zaman türleri kontrol etmek gibi "kaba" bir şey yapmıyorsunuz. Kaçınılmaz tipte kontroller kütüphanenin içinde yapılır. Haskell'in, herhangi bir monad veya functor üzerinde çalışacak büyük işlevler setinden bahsetmemek için kendi benzer işlevleri vardır.

Diğer cebirsel veri türlerinin onlarla çalışmak için kendi deyimsel yolları vardır ve çoğu durumda totem kutbunda desen eşleşmesi düşüktür. Benzer şekilde, kendi türlerinizi oluşturduğunuzda, bunlarla çalışmak için benzer işlevler sağlamanız beklenir.

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.