Dinamik olarak yazılmış bir dilde farklı veri türlerini tek bir işlevden döndürmek kötü bir fikir midir?


65

Ana dilim statik olarak yazılmış (Java). Java'da, her yöntemden tek bir tür döndürmeniz gerekir. Örneğin, koşullu olarak a döndüren Stringveya koşullu olarak a döndüren bir yönteme sahip olamazsınız Integer. Ancak, örneğin JavaScript'te bu çok mümkün.

Statik olarak yazılmış bir dilde bunun neden kötü bir fikir olduğunu anlıyorum. Her yöntem geri dönerse Object(ortak ebeveyn tüm sınıflardan miras kalırsa ), o zaman siz ve derleyici neyle uğraştığınızı bilmiyorsunuz. Tüm hatalarını çalışma zamanında keşfetmen gerekecek.

Ancak dinamik olarak yazılmış bir dilde, bir derleyici bile olmayabilir. Dinamik olarak yazılmış bir dilde, neden birden çok tür döndüren bir işlevin kötü bir fikir olduğu açık değildir. Statik dillerdeki geçmişim, bu tür işlevler yazmaktan kaçınmama neden oluyor, ancak kodumu görememediğim şekilde daha temiz hale getirebilecek bir özellik konusunda yakından ilgilendiğimden korkuyorum.


Düzenleme : Örneğimi kaldıracağım (daha iyisini düşünene kadar). İnsanları yapmaya çalışmadığım bir noktaya cevap vermeye zorluyorum.


Neden hata durumunda bir istisna atmıyorsunuz?
TrueWill

1
@TrueWill bunu bir sonraki cümleyle ele alacağım.
Daniel Kaplan,

Bunun daha dinamik dillerde yaygın bir uygulama olduğunu fark ettiniz mi? İşlevsel dillerde yaygındır , ancak kurallar çok açık bir şekilde belirtilmiştir. Ve özellikle JavaScript'te, argümanların farklı türler olmasına izin vermenin yaygın olduğunu biliyorum (aslında bu işlev aşırı yüklemesini yapmanın tek yolu budur), ancak bunun değerleri döndürmek için uygulandığını nadiren gördüm. Düşünebildiğim tek örnek, her yerde çoğunlukla otomatik olarak dizilen / paketlenen otomatik dizilimli PowerShell'dir ve komut dosyası dilleri bir istisnai durumdur.
Aaron

3
Bahsedilmedi, ancak parametre olarak bir geri dönüş tipi alan birçok örnek (Java Jeneriklerinde bile) var; Common Lisp örneğin elimizdeki (coerce var 'string)verimleri a stringveya (concatenate 'string this that the-other-thing)aynı şekilde. Ben de böyle şeyler yazdım ThingLoader.getThingById (Class<extends FindableThing> klass, long id). Ve orada, sadece istediğiniz şeyi alt sınıflara loader.getThingById (SubclassA.class, 14)döndüren bir SubclassBşeyi iade edebilirim : uzayan bir şeyi geri getirebilir SubclassA...
BRPocock

1
Dinamik olarak yazılmış diller, The Matrix filmindeki Kaşık gibidir . Kaşık bir dize veya sayı olarak tanımlamaya çalışmayın . Bu imkansız olurdu. Bunun yerine ... sadece gerçeği anlamaya çalışın. Kaşık yok ki .
Tepsi

Yanıtlar:


42

Diğer cevapların aksine, farklı türlerin geri gönderilmesinin kabul edilebilir olduğu durumlar vardır.

örnek 1

sum(2, 3)  int
sum(2.1, 3.7)  float

Statik olarak yazılmış bazı dillerde, bu aşırı yüklenmeleri içerir, bu yüzden her biri önceden tanımlanmış, sabit tipe dönen birkaç yöntem olduğunu düşünebiliriz. Dinamik dillerde, bu aynı şekilde gerçekleştirilebilir:

var sum = function (a, b) {
    return a + b;
};

Aynı fonksiyon, farklı dönüş değerleri.

Örnek 2

Bir OpenID / OAuth bileşeninden bir yanıt aldığınızı hayal edin. Bazı OpenID / OAuth sağlayıcıları, kişinin yaşı gibi daha fazla bilgi içerebilir.

var user = authProvider.findCurrent();
// user is now:
// {
//     provider: 'Facebook',
//     name: {
//         firstName: 'Hello',
//         secondName: 'World',
//     },
//     email: 'hello.world@example.com',
//     age: 27
// }

Diğerleri asgari düzeyde olacaktı, bir e-posta adresi ya da takma ad olurdu.

var user = authProvider.findCurrent();
// user is now:
// {
//     provider: 'Google',
//     email: 'hello.world@example.com'
// }

Yine aynı fonksiyon, farklı sonuçlar.

Burada, farklı türlerin geri döndürülmesinin yararı, türleri ve arayüzleri önemsemediğiniz, ancak gerçekte hangi nesneleri içerdiği bağlamında özellikle önemlidir. Örneğin, bir web sitesinin olgun dil içerdiğini düşünelim. Sonra bu findCurrent()gibi kullanılabilir:

var user = authProvider.findCurrent();
if (user.age || 0 >= 16) {
    // The person can stand mature language.
    allowShowingContent();
} else if (user.age) {
    // OpenID/OAuth gave the age, but the person appears too young to see the content.
    showParentalAdvisoryRequestedMessage();
} else {
    // OpenID/OAuth won't tell the age of the person. Ask the user himself.
    askForAge();
}

Bunu, her sağlayıcının iyi tanımlanmış, sabit bir tür döndürecek kendi işlevine sahip olacağı bir koda yeniden yerleştirmek, yalnızca kod tabanını bozmaz ve kod çoğaltmasına neden olmaz, aynı zamanda herhangi bir fayda getirmez. Birisi gibi dehşetlere neden olabilir:

var age;
if (['Facebook', 'Yahoo', 'Blogger', 'LiveJournal'].contains(user.provider)) {
    age = user.age;
}

5
"Statik olarak yazılmış dillerde, bu aşırı yüklemeleri içerir" bence " statik olarak yazılmış bazı dillerde" demek istediniz. ":) İyi statik olarak yazılmış diller, sumörneğinize benzer bir şey için aşırı yükleme gerektirmez .
Andres F.

17
Belirli Örneğin, Haskell düşünün: sum :: Num a => [a] -> a. Sayı olan herhangi bir şeyin listesini toplayabilirsiniz. Javascript'ten farklı olarak, sayı olmayan bir şeyi toplamaya çalışırsanız, hata derleme zamanında yakalanır.
Andres F.

3
@MainMa Scala'da her türlü sayıyı toplamaya izin veren ve derleme zamanında kontrol edilen bir Iterator[A]yöntem vardır def sum[B >: A](implicit num: Numeric[B]): B.
Petr Pudlák

3
@Bakuriu Elbette böyle bir işlevi yazabilirsiniz, ancak önce +dizgiler için aşırı yükleme yapmanız gerekebilir ( Numtasarım yoluyla , yasal ancak korkunç bir fikir olan uygulama yaparak ) veya tamsayı ve dizgiler tarafından aşırı yüklenmiş farklı bir operatör / işlev icat etmeniz gerekebilir. sırasıyla. Haskell, tip sınıfları aracılığıyla geçici polimorfizm gösterir. Hem tamsayıları hem de karakter dizilerini içeren bir liste daha zordur (dil eklentileri olmadan imkansızdır), fakat oldukça farklı bir konudur.

2
İkinci örneğinizden özellikle etkilenmedim. Bu iki nesne aynı kavramsal "tip" tir, sadece bazı durumlarda belirli alanlar tanımlanmaz veya doldurulmaz. Bunu sadece statik olarak yazılmış bir dilde bile nulldeğerlerle temsil edebilirsin.
Aaron

31

Genel olarak, aynı nedenlerle, statik olarak yazılmış bir dilde ahlaki eşdeğerin kötü bir fikir olduğu için kötü bir fikirdir: Hangi somut türün döndürüldüğü hakkında hiçbir fikriniz yoktur, bu nedenle, sonuçla ne yapabileceğiniz hakkında hiçbir fikriniz yoktur. herhangi bir değerle yapılabilir birkaç şey). Statik tip bir sistemde, derleyici tarafından kontrol edilen iade türleri ek açıklamalarına sahipsiniz, ancak dinamik bir dilde aynı bilgi hala var - bu sadece gayri resmi ve kaynak kodundan ziyade beyin ve belgelerde saklanıyor.

Bununla birlikte, birçok durumda, kafiyeli ve tipin geri dönmesinin nedeni vardır ve etkisi statik tip sistemlerde aşırı yüklenmeye veya parametrik polimorfizme benzerdir. Başka bir deyişle, sonuç türü olan sadece değil oldukça basit ifade etmek var, öngörülebilir.

Ancak, belirli bir işlevin kötü bir şekilde tasarlanmasının başka nedenleri olabileceğine dikkat edin: Örneğin, sumgeçersiz girdilerde yanlış dönen bir işlev, temel olarak kötü bir fikirdir, çünkü bu geri dönüş değeri işe yaramaz ve hataya açıktır (0 <-> yanlış karışıklık).


40
İki sentim: Bu olduğu zaman nefret ediyorum. Sonuç olmadığında boşa dönen JS kütüphanelerini, bir sonuçtaki bir Nesneyi ve iki veya daha fazla sonuçtaki bir Diziyi gördüm. Bu yüzden, sadece bir dizi içinde döngü yapmak yerine, boş olup olmadığını ve eğer değilse, bir dizi ise ve eğer bir şey yaparsanız başka bir şey yapın. Özellikle, normal anlamda, herhangi bir mantıklı geliştirici, Nesneyi Diziye ekleyecek ve program mantığını bir diziyi işlemden geri dönüştürecek.
phyrfox

10
@ İzkata: Bence NaN" kontrast " hiç sanmıyorum . NaN aslında herhangi bir kayan nokta hesaplaması için geçerli bir girdidir. NaNGerçek sayı ile yapabileceğin her şeyi yapabilirsin. Doğru, söz konusu hesaplamanın sonucu sizin için çok faydalı olmayabilir, ancak garip çalışma zamanı hatalarına neden olmaz ve her zaman kontrol etmeniz gerekmez - sadece bir defa kontrol edebilirsiniz. hesaplamalar dizisi. NaN farklı bir tip değil , sadece Null Object gibi özel bir değer .
Aaron,

5
@Aaronaught Öte yandan, NaNnull nesne gibi, hatalar ortaya çıktığında, sadece daha sonra ortaya çıkmaları için gizlenme eğilimindedir. Örneğin, programınız NaNkoşullu bir döngüye girdiğinde patlayabilir .
Izkata

2
@ Izkata: NaN ve null tamamen farklı davranışlara sahiptir. Boş bir referans erişimden hemen sonra patlar, bir NaN çoğalır. Sonuncunun ne olduğu açıktır, her alt ifadenin sonucunu kontrol etmek zorunda kalmadan matematiksel ifadeler yazmanıza izin verir. Şahsen ben daha fazla zarar verici buluyorum çünkü NaN boşuna gidemeyeceğiniz doğru ve nümerik bir kavramı temsil ediyor, matematiksel olarak çoğaltmak doğru davranış.
Phoshi

4
Bunun neden "sıfır vs. her şey" argümanına dönüştüğünü bilmiyorum. Sadece NaNdeğerin (a) aslında farklı bir geri dönüş türü olmadığını ve (b) iyi tanımlanmış kayan nokta anlambilimine sahip olduğunu ve bu nedenle gerçekten değişken bir şekilde yazılan geri dönüş değerine bir analog olarak nitelendirilmediğini işaret ediyordum . Gerçekten de tamsayı aritmetik sıfırdan farklı değildir ; sıfırla "yanlışlıkla" hesaplamalarınıza kayarsanız, o zaman sonuç olarak sıfıra ya da sıfıra bölme hatasıyla sonuçlanırsınız. IEEE kayan nokta tarafından tanımlanan "sonsuzluk" değerleri de kötü mü?
Aaron, Jan'e

26

Dinamik dillerde, farklı türlerin döndürülüp döndürülmediğini sormamanız gerekir, ancak farklı API'ler içeren nesneler . Çoğu dinamik dil, türleri gerçekten önemsemediğinden, ancak ördek yazmanın çeşitli sürümlerini kullanın .

Farklı tipler döndürürken anlamlı olun

Örneğin, bu yöntem anlamlıdır:

def load_file(file): 
    if something: 
       return ['a ', 'list', 'of', 'strings'] 
    return open(file, 'r')

Çünkü hem dosya hem de dizge listesi (Python'da) string döndüren yinelemelerdir. Çok farklı türler, aynı API (birisi listedeki dosya yöntemlerini çağırmaya çalışmadığı sürece, ancak bu farklı bir hikaye).

Koşullu olarak geri döndürebilirsiniz listveya tuple( tuplepython'da değişmez bir listedir).

Resmen bile yapıyor:

def do_something():
    if ...: 
        return None
    return something_else

veya:

function do_something(){
   if (...) return null; 
   return sth;
}

python Noneve Javascript null, kendi başlarına birer tür olduklarından , farklı türler döndürür .

Bütün bu kullanım durumları, meslektaşılarını statik dillerde kullanır, fonksiyon sadece uygun bir arayüz döndürürdü.

Koşullu olarak farklı API içeren nesneleri döndürürken iyi bir fikirdir

Farklı API'lerin döndürülmesinin iyi bir fikir olup olmadığı konusunda, IMO çoğu durumda bir anlam ifade etmemektedir. Akla gelen tek mantıklı örnek @MainMa'nın söylediğine yakın bir şeydir : API'niz değişken miktarda ayrıntı sağlayabiliyorsa, mümkün olduğunda daha fazla ayrıntı döndürmek mantıklı olabilir.


4
İyi cevap. Java / statik olarak yazılan eşdeğerinin bir işleve sahip olmak olduğuna dikkat çekmeye değer, belirli bir somut tür yerine bir arabirim döndürür, arabirim API / soyutlamayı temsil eder.
mikera

1
Bence, nittikleri çalıyorsunuz, Python'da bir liste ve bir tuple farklı "türler" olabilir, ancak bunlar aynı "ördek türünden", yani aynı işlemleri destekliyorlar. Ve bunun gibi durumlar tam da ördek tiplendirmenin tanıtılmasının nedenidir. Sen de x = getInt () Java ile yapabilirsiniz.
Kaerber

“Farklı türlerin döndürülmesi” “farklı API'ler içeren nesnelerin döndürülmesi” anlamına gelir . (Bazı diller nesnenin API'nin temel bir parçasını oluşturmasını sağlar, bazıları değildir; bu, tip sisteminin ne kadar etkileyici olduğu ile ilgilidir.) Liste / dosya örneğinizde, do_fileher iki şekilde de yinelenebilir bir sonuç döndürür.
Gilles

1
Bu Python örneğinde, iter()sonucun yalnızca her iki durumda da yineleyici olarak kullanılabileceğinden emin olmak için hem listeyi hem de dosyayı arayabilirsiniz .
RemcoGerlich

1
geri dönen None or somethingPyPy, Numba, Pyston ve diğerleri gibi araçlar tarafından yapılan performans optimizasyonu için bir katil. Bu, python'u o kadar hızlı olmayan yapan Python-ism'den biridir.
Matt

9

Sorunuz biraz ağlamak istememi sağlıyor. Sağladığınız örnek kullanım için değil, birileri istemeden bu yaklaşımı çok ileri götürecek. Bu gülünç bir şekilde sürdürülemez koddan kısa bir adım.

Hata durumu, büyük / küçük harf kullanımı anlamlıdır ve statik olarak yazılan dillerdeki boş kalıp (her şey bir kalıp olmalıdır) aynı şeyi yapar. İşlev çağrınız objectveya döner null.

Ama "Ben oluşturmak için bu kullanmak için gidiyorum diyerek kısa bir adımdır fabrika deseni ve dönüş" ya fooya barya bazişlevin ruh bağlı. Bu hata ayıklama, arayan beklediği fooancak verildiğinde bir kabus olur bar.

Bu yüzden fikirlerin kapalı olduğunu sanmıyorum. Dilin özelliklerinin kullanımında uygun bir şekilde temkinli davranıyorsunuz.

Açıklama: arkaplanım statik olarak yazılmış dillerde ve genel olarak bakılabilir kod ihtiyacının oldukça yüksek olduğu daha geniş ve çeşitli ekipler üzerinde çalıştım. Yani benim bakış açım da muhtemelen eğri.


6

Generics'i Java'da kullanmak, statik tür güvenliğini korurken, farklı bir tür döndürmenize olanak tanır. İşlev çağrısının genel tür parametresinde döndürmek istediğiniz türü belirtmeniz yeterlidir.

Javascript'te de benzer bir yaklaşım kullanıp kullanamayacağınız elbette açık bir sorudur. Javascript dinamik olarak yazılmış bir dil olduğundan, geri dönüşü objectaçık bir seçim gibi görünüyor.

Statik olarak yazılmış bir dilde çalışmaya alışkın olduğunuzda dinamik bir geri dönüş senaryosunun nerede işe yaradığını bilmek istiyorsanız dynamic, C # anahtar kelimesine bakmayı düşünün . Rob Conery, anahtar kelime kullanarak 400 kod satırında bir Nesne İlişkisel Eşleştiricisi başarıyla yazabildidynamic .

Tabii ki, hepsi dynamicgerçekten, objectbazı çalışma zamanı tür güvenliği ile bir değişken sarmaktır.


4

Bence şartlı olarak farklı türler geri dönmek kötü bir fikir. Bunun benim için sık sık ortaya çıktığı yollardan biri, işlevin bir veya daha fazla değer döndürmesidir. Yalnızca bir değerin geri döndürülmesi gerekiyorsa, çağıran işlevde paketini açmak zorunda kalmamak için, bir Dizi'ye paketlemek yerine, yalnızca değeri döndürmek makul görünebilir. Bununla birlikte, bu (ve bunun diğer pek çok örneği), arayana her iki türü birbirinden ayırt etme ve kullanma zorunluluğu getirmektedir. Fonksiyonun daima aynı tipte dönüp dönmediğini düşünmesi daha kolay olacaktır.


4

Dilinizin statik olarak yazılmış olup olmadığına bakılmaksızın "kötü uygulama" vardır. Statik dil sizi bu uygulamalardan uzak tutmak için daha fazlasını yapar ve statik bir dilde "kötü uygulama" hakkında şikayet eden daha fazla kullanıcı bulabilirsiniz çünkü bu daha resmi bir dildir. Ancak, altta yatan problemler dinamik bir dilde var ve haklı olup olmadıklarını belirleyebilirsiniz.

İşte teklif ettiğiniz şeyin sakıncalı kısmı. Hangi türün döndüğünü bilmiyorsam, dönüş değerini hemen kullanamıyorum. Bununla ilgili bir şeyi "keşfetmek" zorundayım.

total = sum_of_array([20, 30, 'q', 50])
if (type_of(total) == Boolean) {
  display_error(...)
} else {
  record_number(total)
}

Genellikle koddaki bu tür bir anahtar sadece basit bir uygulamadır. Kodu okumayı zorlaştırır. Bu örnekte, istisna vakalarını atma ve yakalamanın neden popüler olduğunu görebilirsiniz. Başka bir yolla: eğer işleviniz söylediğini yapamazsa, başarılı bir şekilde geri dönmemelidir . İşlevinizi ararsam, bunu yapmak istiyorum:

total = sum_of_array([20, 30, 'q', 50])
display_number(total)

İlk satır başarılı bir şekilde döndüğü totaliçin, dizinin toplamını içerdiğini sanıyorum . Başarıyla dönmezse, programımın başka bir sayfasına atlıyoruz.

Sadece hataları yaymakla ilgili olmayan başka bir örnek kullanalım. Belki sum_of_array, bazı durumlarda "Bu benim soyunma kombinasyonum!" Gibi akıllı olmaya ve insan tarafından okunabilen bir string döndürmeye çalışır. eğer ve sadece dizi [11,7,19] ise. İyi bir örnek düşünmekte zorlanıyorum. Neyse, aynı problem geçerli. Bununla bir şey yapmadan önce dönüş değerini kontrol etmelisin:

total = sum_of_array([20, 30, 40, 50])
if (type_of(total) == String) {
  write_message(total)
} else {
  record_number(total)
}

İşlevin bir tamsayı ya da bir şamandıra döndürmesi faydalı olacağını iddia edebilirsiniz, örneğin:

sum_of_array(20, 30, 40) -> int
sum_of_array(23.45, 45.67, 67.789044) -> float

Ancak bu sonuçlar sizin ilgilendiğiniz kadar farklı değil. Her ikisine de sayılar gibi davranacaksınız ve tek umursadığınız bu. Böylece sum_of_array sayı türünü döndürür. Polimorfizm budur.

Bu nedenle, işleviniz birden fazla tür döndürürse ihlal edebileceğiniz bazı uygulamalar vardır. Bunları bilmek, belirli bir işlevin yine de birden çok tür döndürmesi gerekip gerekmediğini belirlemenize yardımcı olur.


Üzgünüm, sizi kötü örnek ile yanılttım. Unut gitsin, ilk başta. İlk paragrafınız yerinde. İkinci örneğini de beğendim.
Daniel Kaplan,

4

Aslında, statik olarak yazılmış bir dilde bile farklı türler döndürmek hiç de nadir değildir. Mesela sendika türlerimiz var.

Aslında, Java'daki yöntemler neredeyse her zaman dört türden birini döndürür: bir tür nesne ya nullda bir istisna ya da asla geri dönmezler.

Birçok dilde hata koşulları, sonuç türünü ya da hata türünü döndüren alt yordamlar olarak modellenir. Örneğin, Scala'da:

def transferMoney(amount: Decimal): Either[String, Decimal]

Bu aptalca bir örnek, elbette. Dönüş türü "bir dize ya da ondalık döndür" anlamına gelir. Kurallara göre, sol tür hata türüdür (bu durumda, hata mesajına sahip bir dize) ve sağ tür sonuç türüdür.

Bu, istisnaların aynı zamanda kontrol akışı yapıları olması haricinde, istisnalara benzer. Aslında, ifade gücü bakımından eşdeğerdirler GOTO.


Java döndürme istisna yönteminde garip sesler. " Return bir istisna hmm ..."
sivrisinek

3
İstisna, Java'daki bir yöntemin olası bir sonucudur. Aslında, dördüncü bir türü unuttum: Unit(geri dönmek için hiçbir yöntem gerekli değildir). Doğru, aslında returnanahtar kelimeyi kullanmıyorsunuz , ancak yine de yöntemden gelen bir sonuç. İşaretli istisnalar dışında, tip imzasında açıkça belirtilmiştir.
Jörg W Mittag

Anlıyorum. Amacınızın bazı yararları var gibi gözüküyor, ancak sunulma şekli onu zıt görünmüyor ... aksine
gnat

4
Birçok dilde, hata koşulları, sonuç türünü ya da hata türünü döndüren alt yordam olarak modellenir. İstisnalar, aynı zamanda kontrol akış yapıları ( GOTOaslında, ifade gücüne eşittir) olması dışında benzer bir fikirdir .
Jörg W Mittag

4

SOLID ilkelerinden henüz bir cevap gelmedi. Özellikle, Liskov ikame prensibine uymalısınız, beklenen tipten başka bir tip alan herhangi bir sınıfın, hangi tipin iade edildiğini test etmek için hiçbir şey yapmadan, ne olursa olsun çalışabileceğini unutmayın.

Öyleyse, bir nesneye fazladan özellikler atarsanız ya da orijinal bir fonksiyonun yerine getirmeyi amaçlayan bir dekoratöre geri dönen bir işlevi sararsanız, fonksiyonunuzu çağıran hiçbir kod buna dayanmadığı sürece, iyisinizdir. davranış, herhangi bir kod yolunda.

Bir dize veya tamsayı döndürmek yerine, daha iyi bir örnek bir yağmurlama sistemi veya kedi döndürmesi olabilir. Tüm arayan kod yapacaksa bu işlev iyidir, functionInQuestion.hiss (). Etkili bir şekilde, çağıran kodun beklediği örtük bir arayüze sahipsiniz ve dinamik olarak yazılmış bir dil sizi arayüzü açık hale getirmeye zorlamaz.

Maalesef, iş arkadaşlarınız muhtemelen yapacaklardır, bu nedenle muhtemelen aynı işi yapmak zorundasınız, belgeleriniz için yine de aynı işi yapmak zorundasınız. Bunu yapmak için evrensel olarak kabul edilmiş, kısa, makinede analiz edilebilir bir yol bulunmuyor - bir arayüz tanımladığınızda olduğu gibi Onlara sahip bir dilde.


3

Kendimi farklı türler gönderirken gördüğüm bir yer geçersiz girdi ya da “istisnai durum” un çok istisnai olmadığı "yoksul istisnalar". Örneğin, PHP yardımcı programı havuzundan bu kısaltılmış örnek:

function ensure_fields($consideration)
{
        $args = func_get_args();
        foreach ( $args as $a ) {
                if ( !is_string($a) ) {
                        return NULL;
                }
                if ( !isset($consideration[$a]) || $consideration[$a]=='' ) {
                        return FALSE;
                }
        }

        return TRUE;
}

İşlev nominal olarak bir BOOLEAN döndürür, ancak geçersiz girişte NULL döndürür. PHP 5.3'ten bu yana, tüm PHP işlevlerinin bu şekilde davrandığını unutmayın . Ek olarak, bazı dahili PHP fonksiyonları nominal girişte FALSE veya INT döndürür, bakınız:

strpos('Hello', 'e');  // Returns INT(1)
strpos('Hello', 'q');  // Returns BOOL(FALSE)

4
İşte bu yüzden PHP'den nefret ediyorum. Her neyse, sebeplerden biri.
Aaron,

1
Olumsuz, çünkü biri profesyonel olarak geliştirdiğim dili sevmiyor mu? Yahudi olduğumu öğrenene kadar bekle! :)
dotancohen

3
Her zaman yorum yapanların ve oy verenlerin aynı kişi olduğunu varsaymayın.
Aaron,

1
Bunun yerine bir istisna olmayı tercih ediyorum null, çünkü (a) yüksek sesle başarısız oluyor , bu nedenle geliştirme / test aşamasına sabitlenmesi daha muhtemel, (b) kullanımı kolaydır, çünkü tüm nadir / beklenmedik istisnaları bir kez yakalayabilirim. tüm uygulamam (ön denetleyicimde) ve sadece oturum açın (genellikle dev ekibine e-posta gönderirim), böylece daha sonra düzeltilebilir. Ve ben aslında standart PHP kütüphanesinin çoğunlukla "return null" yaklaşımını kullanmasından nefret ediyorum - sadece PHP kodunu hataya açık hale getiriyor, her şeyi kontrol etmiyorsanız isset(), ki bu çok fazla yük.
Scriptin

1
Ayrıca, nullhataya geri dönerseniz , lütfen bunu lütfen bir docblock (örn. @return boolean|null) İçinde açıkça belirtin , bu nedenle bir gün kodunuzla karşılaşırsam, işlev / yöntem gövdesini kontrol etmek zorunda kalmam.
scriptin

3

Bunun kötü bir fikir olduğunu sanmıyorum! Bu en yaygın görüşün aksine ve Robert Harvey tarafından da belirtildiği gibi, Java gibi Statik olarak yazılmış diller, tam olarak sorduğunuz gibi durumlar için Generics'i tanıttı. Aslında Java derleme zamanında (mümkün olan her yerde) yazı güvenliğini korumaya çalışır ve bazen Generics kod kopyalamayı engeller, neden? Çünkü aynı yöntemi ya da farklı türleri işleyen / döndüren aynı sınıfı yazabilirsiniz . Bu fikri göstermek için çok kısa bir örnek vereceğim:

Java 1.4

public static Boolean getBoolean(String property){
    return (Boolean) properties.getProperty(property);
}
public static Integer getInt(String property){
    return (Integer) properties.getProperty(property);
}

Java 1.5+

public static <T> getValue(String property, Class<T> clazz) throws WhateverCheckedException{
    return clazz.getConstructor(String.class).newInstance(properties.getProperty(property));
}
//the call will be
Boolean b = getValue("useProxy",Boolean.class);
Integer b = getValue("proxyPort",Integer.class);

Dinamik bir yazılı dilde, derleme zamanında yazı tipi güvenceniz olmadığından, birçok yazı tipi için aynı kodu yazmakta kesinlikle özgürsünüz. Statik olarak yazılmış bir dilde bile, Generics bu sorunu çözmek için tanıtıldığından, farklı türde dinamik bir dilde dönen bir fonksiyon yazmanın kötü bir fikir olmadığı açıkça görülüyor.


Açıklama olmadan başka bir aşağı oy! Teşekkürler SE topluluğu!
termz

2
Bir sempati duymak +1. Yanıtınızı daha net ve doğrudan bir cevapla açmayı denemeli ve farklı cevaplar hakkında yorum yapmaktan kaçınmalısınız. (Size başka bir + 1 veririm, çünkü seninle aynı fikirdeyim, ama sadece verecek bir tane var.)
DougM

3
Jenerikler dinamik olarak yazılmış dillerde (bildiğim kadarıyla) mevcut değil. Onlar için hiçbir sebep olmazdı. Çoğu jenerik, "container" ın, içinde olduğundan daha ilginç olduğu özel bir durumdur (bu nedenle, Java gibi bazı diller aslında silme türünü kullanır). Genel tür aslında kendine özgü bir türdür. Buradaki örneğinizde olduğu gibi, gerçek bir jenerik tip olmayan jenerik yöntemler , hemen hemen her zaman sadece ata ve kullan anlamında kullanılan bir sözdizimi şekeridir. Sorunun gerçekte ne sorduğuna dair özellikle çarpıcı bir örnek değil.
Aaron,

1
Biraz daha sentetik olmaya çalışıyorum: “Statik olarak yazılmış bir dilde bile, bu sorunu çözmek için Generics tanıtıldığından, farklı türde dinamik bir dilde dönen bir işlev yazmanın kötü bir fikir olmadığı açıkça görülüyor.” Şimdi daha iyi? Robert Harvey'in cevabını veya BRPocock'un yorumunu kontrol edin, Generics örneğinin bu soruyla ilgili olduğunu fark edeceksiniz
thermz

1
Kötü hissetme, @thermz. Düşüşler genellikle okuyucu hakkında yazardan daha fazla şey söyler.
Karl Bielefeldt

2

Yazılım geliştirme temel olarak bir sanat eseri ve karmaşıklığı yönetme sanatıdır. Sistemi alabileceğiniz noktalarda daraltmaya ve seçenekleri diğer noktalarda sınırlamaya çalışıyorsunuz. İşlev arayüzü, herhangi bir kod parçasıyla çalışmak için gereken bilgileri sınırlayarak kodun karmaşıklığını yönetmeye yardımcı olan sözleşmedir. Farklı türleri döndürerek, fonksiyonun arayüzünü, ona döndürdüğünüz farklı türlerin tüm arayüzlerini ekleyerek ve hangi arayüzün döndürüleceği ile ilgili açık olmayan kurallar ekleyerek önemli ölçüde genişletirsiniz.


1

Perl bunu çok kullanır, çünkü bir işlevin yaptığı şey içeriğine bağlıdır . Örneğin, bir işlev liste bağlamında kullanılırsa bir dizi döndürebilir veya skaler değerin beklendiği bir yerde kullanılırsa dizinin uzunluğunu döndürebilir. Gönderen "perl bağlamda" için ilk isabet öğretici bunu yaparsanız,:

my @now = localtime();

Sonra @now bir dizi değişkenidir (@, bu anlama gelir) ve şöyle bir dizi içerecektir (40, 51, 20, 9, 0, 109, 5, 8, 0).

Bunun yerine, işlevi sonucunun skalar olması gerektiği şekilde çağırırsanız, ($ değişkenleri skalerdir):

my $now = localtime();

o zaman tamamen farklı bir şey yapar: $ şimdi "cuma 9 Ocak 20:51:40 2009" gibi bir şey olacak.

Aklıma gelen başka bir örnek, REST API'lerini uygulamakta olup, iade edilenin formatı müşterinin istediğine bağlıdır. Örneğin, HTML veya JSON veya XML. Teknik olarak bunların hepsi bayt akışları olsa da, fikir benzer.


Bunu perl - wantarray olarak yapmanın özel yolunu ve belki iyi ya da kötü ise bazı tartışmalara bir bağlantıdan bahsetmek isteyebilirsiniz - wantarray'ın kullanımı PerlMonks'ta Zararlı olarak kabul edilir

Tesadüf eseri, bununla inşa ettiğim bir Java Rest API'sı ile uğraşıyorum. Bir kaynağın XML veya JSON döndürmesini mümkün kıldım. Bu durumda en düşük ortak payda tipi a'dır String. Şimdi insanlar geri dönüş tipi ile ilgili belgeler istiyorlar. Bir sınıf döndürürsem java araçları otomatik olarak oluşturabilir, ancak seçtiğim Stringiçin belgeleri otomatik olarak oluşturamıyorum. Hikayenin görüş / ahlakında, yöntem başına bir tür geri dönmesini diliyorum.
Daniel Kaplan,

Kesinlikle, bu bir aşırı yüklenme şekline gelir. Başka bir deyişle, çok sayıda federasyon işlevi vardır ve aramanın özelliklerine bağlı olarak, bir veya diğer sürüm seçilir.
Ian

1

Dinamik topraklarda hepsi ördek yazma ile ilgilidir. Yapılacak en sorumlu maruz kalan / halka açık olan şey, potansiyel olarak farklı türleri, aynı arayüzü veren bir sargıya sarmaktır.

function ThingyWrapper(thingy){ //a function constructor (class-like thingy)

    //thingy is effectively private and persistent for ThingyWrapper instances

    if(typeof thingy === 'array'){
        this.alertItems = function(){
            thingy.forEach(function(el){ alert(el); });
        }
    }
    else {
        this.alertItems = function(){
            for(var x in thingy){ alert(thingy[x]); }
        }
    }
}

function gimmeThingy(){
    var
        coinToss = Math.round( Math.random() ),//gives me 0 or 1
        arrayThingy = [1,2,3],
        objectThingy = { item1:1, item2:2, item3:3 }
    ;

    //0 dynamically evaluates to false in JS
    return new ThingyWrapper( coinToss ? arrayThingy : objectThingy );
}

gimmeThingy().alertItems(); //should be same every time except order of numbers - maybe

Zaman zaman farklı türleri tamamen genel bir ambalajlayıcı olmadan tamamen dışa vurmak mantıklı gelebilir, ancak dürüstçe JS yazarken dürüstçe, çok sık yapmanın makul ya da uygun olduğunu düşündüğüm bir şey değil. Çoğunlukla bu, nesnelerin bir araya getirildiği iç nesneler gibi kapalı ortamlar bağlamında yaptığım bir şey. Ancak bu, sık sık yaptığım hiçbir şeyin aklınıza gelmeyeceği bir şey değil.

Çoğunlukla, türler hakkında tamamen düşünmeyi bırakmanızı tavsiye ederim. Dinamik bir dilde gerektiğinde türlerle uğraşırsınız. Daha fazla yok. Bütün mesele bu. Her argümanın türünü kontrol etmeyin. Bu, aynı yöntemin açık olmayan şekillerde tutarsız sonuçlar verebileceği bir ortamda yapmam için cazip olacağım bir şey (kesinlikle böyle bir şey yapmayın). Ama önemli olan tip değil, bana verdiğin iş bu.

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.