Hangi statik olarak yazılan diller işlev dönüş değerleri için kavşak türlerini destekler?


17

İlk not:

Bu soru birkaç düzenlemeden sonra kapatıldı, çünkü aradığım şeyi doğru bir şekilde ifade etmek için uygun terminolojiden yoksundum. Sam Tobin-Hochstadt daha sonra tam olarak ne olduğunu anlamamı sağlayan bir yorum yayınladı: fonksiyon dönüş değerleri için kavşak tiplerini destekleyen programlama dilleri.

Şimdi soru yeniden açıldığına göre, (umarım) daha kesin bir şekilde yeniden yazarak soruyu geliştirmeye karar verdim. Bu nedenle, aşağıdaki bazı yanıtlar ve yorumlar artık önceki düzenlemelere atıfta bulundukları için mantıklı olmayabilir. (Lütfen bu gibi durumlarda sorunun düzenleme geçmişine bakın.)

İşlev döndürme değerleri için kesişim türlerini destekleyen popüler statik ve kuvvetle yazılan programlama dilleri (Haskell, genel Java, C #, F # vb.) Var mı ? Varsa, hangisi ve nasıl?

(Dürüst olursam, birisinin C # veya Java gibi anaakım bir dilde kesişim türlerini nasıl ifade etmenin bir yolunu gösterdiğini görmek isterim.)

Ben C # benzer bazı sözde kodunu kullanarak, kavşak türleri nasıl görünebilir hızlı bir örnek vereceğim:

interface IX { … }
interface IY { … }
interface IB { … }

class A : IX, IY { … }
class B : IX, IY, IB { … }

T fn()  where T : IX, IY
{
    return … ? new A()  
             : new B();
}

Olduğunu, fonksiyon fnbazı türünde bir örnek verir Tarayan o arabirimleri uygulayan tek bildiği üzere, IXve IY. (Yani, jeneriklerden farklı olarak, arayan somut türü seçemez T- işlev yapar. Bundan T, aslında evrensel bir tür değil, varoluşsal bir tür olduğunu varsayalım .)

Not: Bir basit bir tanımlayabiliriz farkındayım interface IXY : IX, IYve dönüş türünü değiştirmek fniçin IXY. Bununla birlikte, bu gerçekten aynı şey değildir, çünkü genellikle ek bir arabirime yalnızca IXYönceden ve ayrı olarak Auygulanan daha önce tanımlanmış bir türe cıvatalayamazsınız .IXIY


Dipnot: Kavşak tipleri ile ilgili bazı kaynaklar:

"Tip sistemi" için Wikipedia makalesinde kesişim türleri hakkında bir alt bölüm vardır .

Benjamin C. Pierce (1991), "Kavşak Tipleri, Birlik Tipleri ve Çok Biçimlilikle Programlama" Raporu

David P. Cunningham (2005), "Uygulamada kavşak türleri" , Wikipedia makalesinde bahsedilen Forsythe dili hakkında bir vaka çalışması içerir.

Bir yığın taşması soru "Birlik türleri ve kavşak türleri" aralarında birkaç iyi cevap var bu bir yukarıda mayın benzer kavşak tipleri yalancı kod örnek verir.


6
Bu nasıl belirsiz? Tsadece "/ uygular uzanan bir tür olarak işlev bildiriminde tanımlanan olsa bile, bir tür tanımlar IXve IY". Aslında gerçek dönüş değeri bunun özel bir durumudur ( Aveya Bsırasıyla) sadece de kullanarak kazanmasını sağlayabilecek, burada özel bir şey değil Objectyerine T.
Joachim Sauer

1
Ruby bir işlevden ne istersen dönmene izin verir. Diğer dinamik diller için aynıdır.
thorsten müller

Cevabımı güncelledim. @Joachim: "Belirsiz" teriminin söz konusu kavramı tam olarak yakalamadığının farkındayım, dolayısıyla amaçlanan anlamı açıklığa kavuşturmak için örnek.
stakx

1
Reklam PS: ... sorunuzu "hangi dil, Tarayüzün Itüm yöntemlerini uyguladığında, ancak bu arayüzü bildirmediğinde, arayüzün işlem görmesine izin verdiğini" olarak değiştirir.
Jan Hudec

6
Bu soruyu kapatmak bir hataydı, çünkü sendika türleri olan kesin bir cevap var . Birlik türleri ( Tipli Raket) [ docs.racket-lang.org/ts-guide/] gibi dillerde mevcuttur .
Sam Tobin-Hochstadt

Yanıtlar:


5

Scala, dilde yerleşik tam kavşak tiplerine sahiptir:

trait IX {...}
trait IY {...}
trait IB {...}

class A() extends IX with IY {...}

class B() extends IX with IY with IB {...}

def fn(): IX with IY = if (...) new A() else new B()

dotty tabanlı scala gerçek kavşak tiplerine sahip olacaktı, ancak mevcut / eski scala için değil.
Hongxu Chen

9

Aslında, bariz cevap: Java

Java'nın kavşak tiplerini desteklediğini öğrenmek sizi şaşırtsa da, aslında "&" tipine bağlı operatör aracılığıyla bunu yapar. Örneğin:

<T extends IX & IY> T f() { ... }

Java'daki birden çok tür sınırında bu bağlantıya ve ayrıca Java API'sında da bu bağlantıya bakın .


Derleme zamanında türü bilmiyorsanız bu işe yarayacak mı? Yani yazabilirim <T extends IX & IY> T f() { if(condition) return new A(); else return new B(); }. Ve böyle bir durumda işlevi nasıl çağırırsınız? Çağrı sitesinde ne A ne de B görünemez, çünkü hangisini alacağınızı bilmiyorsunuz.
Jan Hudec

Evet, haklısınız - somut bir tür sağlamanız gerektiğinden verilen orijinal örneğe eşdeğer değil. Kavşak sınırları olan joker karakterleri kullanabilseydik, o zaman olurdu. Yapamayız gibi görünüyor ... ve gerçekten (göremiyorum neden bilmiyorum bu ). Ama yine de Java bir çeşit kavşak tipine sahip ...
redjamjar

1
Java yaptığım 10 yıl içinde kavşak türlerini bir şekilde öğrenmediğim için hayal kırıklığına uğradım. Şimdi her zaman Flowtype ile kullandığım için, Java'daki en büyük eksik özellik olduğunu düşündüm, sadece onları bir kez vahşi doğada görmemiştim. İnsanlar onları ciddiye alıyorlar. Bence daha iyi bilinselerdi, Spring gibi bağımlılık enjeksiyon çerçeveleri hiç bu kadar popüler olmazdı.
Andy

8

Orijinal soru "belirsiz tip" istedi. Bunun cevabı şuydu:

Belirsiz tip, açıkçası yok. Arayanın ne alacağını bilmesi gerekir, bu yüzden mümkün değildir. Dönebilen tüm diller ya temel tür, arayüz (muhtemelen kavşak türünde olduğu gibi otomatik olarak oluşturulur) ya da dinamik türdür (ve dinamik tür temel olarak sadece ad-çağrısı, alma ve ayarlama yöntemleriyle yazılır).

Çıkarılan arayüz:

Yani temelde IXYtüretilmiş bir arabirim döndürmesini istersiniz IX ve IY bu arabirim ikisinden birinde Aveya Bmuhtemelen bu türler tanımlandığında bildirilmediğinden dolayı bildirilmez. Bu durumda:

  • Açıkçası, dinamik olarak yazılmış herhangi bir.
  • Herhangi bir statik olarak yazılan ana akım dili arayüzü (ve sendika türü Ave / Bveya kavşak türü IXve IY) kendisi oluşturmak mümkün olacağını hatırlamıyorum .
  • GO , çünkü sınıflar, herhangi bir şekilde bildirmeden doğru yöntemlere sahiplerse arabirimi uygularlar. Yani ikisini orada türeyen bir arayüz beyan edip geri veriyorsunuz.
  • Açıkçası bu tür tanım dışında arayüz uygulamak için tür tanımlanabilir başka bir dil, ama GO dışında herhangi bir hatırlıyorum sanmıyorum.
  • Bir arabirim uygulamanın tip tanımlamasında tanımlanması gereken hiçbir türde mümkün değildir. Bununla birlikte, iki arabirimi uygulayan ve tüm yöntemleri sarılmış bir nesneye devreden sarıcı tanımlayarak çoğunda geçici çözüm bulabilirsiniz.

PS A şiddetle yazılan dil ise verilen tipte bir nesne, başka bir tür nesnesi olarak ele alınamaz hangi biridir zayıf yazılan dil bir reinterpret döküm sahiptir biridir. Böylece, dinamik olarak yazılan tüm diller güçlü bir şekilde yazılırken , zayıf yazılan diller C, C ++ derlemesidir, üçü de statik olarak yazılmıştır .


Bu doğru değil, tür hakkında belirsiz bir şey yok. Bir dil "kavşak türleri" olarak adlandırılabilir - herhangi bir ana dil bunu yaparsa, bu kadar azdır.
redjamjar

@redjamjar: Cevap verdiğimde ve "belirsiz tip" istediğimde soru farklı ifadelere sahipti. Bu yüzden bununla başlar. Soru o zamandan beri önemli ölçüde yeniden yazıldı. Cevabı hem orijinal hem de güncel ifadelerden bahsetmek için genişleteceğim.
Jan Hudec

üzgünüm, bunu açıkça özledim!
redjamjar

Muhtemelen buna izin veren ortak bir dilin en iyi örneği olan Golang'dan bahsetmek için +1, bunu yapmanın yolu biraz dolambaçlı olsa bile.
Jules

3

Git Programlama Dili bu tür bir arayüze sahiptir, ancak sadece arayüz tipleri için.

Go'da doğru yöntemlerin tanımlandığı herhangi bir tür otomatik olarak bir arabirim uygular, bu nedenle PS'nizdeki itiraz geçerli değildir. Başka bir deyişle, birleştirilecek arabirim türlerinin tüm işlemlerini içeren bir arabirim oluşturun (bunun için basit bir sözdizimi vardır) ve hepsi Sadece Çalışır.

Bir örnek:

package intersection

type (
    // The first component type.
    A interface {
        foo() int
    }
    // The second component type.
    B interface {
        bar()
    }

    // The intersection type.
    Intersection interface {
        A
        B
    }
)

// Function accepting an intersection type
func frob(x Intersection) {
    // You can directly call methods defined by A or B on Intersection.
    x.foo()
    x.bar()

    // Conversions work too.
    var a A = x
    var b B = x
    a.foo()
    b.bar()
}

// Syntax for a function returning an intersection type:
// (using an inline type definition to be closer to your suggested syntax)
func frob2() interface { A; B } {
    // return something
}

3

İstediğinizi, jenerikler ve sınırlı polimorfizm, örneğin C # ile herhangi bir dilde kodlanabilen sınırlı varoluşsal bir tür kullanarak yapabilirsiniz.

Dönüş türü (psuedo kodunda) gibi bir şey olacaktır

IAB = exists T. T where T : IA, IB

veya C # ile:

interface IAB<IA, IB>
{
    R Apply<R>(IABFunc<R, IA, IB> f);
}

interface IABFunc<R, IA, IB>
{
    R Apply<T>(T t) where T : IA, IB;
}

class DefaultIAB<T, IA, IB> : IAB<IA, IB> where T : IA, IB 
{
    readonly T t;

    ...

    public R Apply<R>(IABFunc<R, IA, IB> f) {
        return f.Apply<T>(t);
    }
}

Not: Bunu test etmedim.

Mesele şu ki IAB, herhangi bir dönüş türü için bir IABFunc uygulayabilmeli Rve hem ve hem de alt tipler IABFuncüzerinde çalışabilmelidir .TIAIB

Niyet ait DefaultIABsadece varolan sarılmasıdır Thangi alt türlerini IAve IB. Bu farklı olduğunu Not IAB : IA, IBin DefaultIABdaima mevcut bir eklenebilir Tdaha sonra.

Referanslar:


Yaklaşım, T, IA, IB parametrelerine sahip genel bir nesne sarıcı tipini eklediğinde, T, arabirimlerle sınırlandırılmış ve T tipinde bir referansı içine alan ve Applyüzerine çağrılmasına izin veren arayüzler eklerse işe yarar . Büyük sorun, bir arabirimi uygulamak için anonim bir işlev kullanmanın bir yolu olmamasıdır, bu nedenle bu tür yapılar kullanmak için gerçek bir acı olur.
Supercat

3

TypeScript , kavşak türlerini T & U(birleşim türleriyle birlikte) destekleyen başka bir yazılan dildir T | U. Aşağıda dokümantasyon sayfalarından ileri düzey türlerle ilgili bir örnek verilmiştir :

function extend<T, U>(first: T, second: U): T & U {
    let result = <T & U>{};
    for (let id in first) {
        (<any>result)[id] = (<any>first)[id];
    }
    for (let id in second) {
        if (!result.hasOwnProperty(id)) {
            (<any>result)[id] = (<any>second)[id];
        }
    }
    return result;
}

2

Seylan , birinci sınıf sendika ve kavşak tipleri için tam desteğe sahiptir .

Bir birleşim türü olarak X | Yve kavşak türü olarak yazarsınız X & Y.

Daha da iyisi, Seylan bu türler hakkında aşağıdakiler de dahil olmak üzere çok sayıda karmaşık akıl yürütme sunar:

  • Temel örneklemi: örneğin, Consumer<X>&Consumer<Y>aynı tür olarak Consumer<X|Y>ise Consumertersine de olduğu Xve
  • ayrıklık: örneğin, alt türleObject&Null aynı Nothingtürdür.

0

C ++ işlevlerinin tümü sabit bir dönüş türüne sahiptir, ancak işaretçiler döndürürlerse, işaretçiler kısıtlamalarla farklı türlere işaret edebilir.

Misal:

class Base {};
class Derived1: public Base {};
class Derived2: public Base{};

Base * function(int derived_type)
{
    if (derived_type == 1)
        return new Derived1;
    else
        return new Derived2;
}

Döndürülen işaretçinin davranışı, hangi virtualişlevlerin tanımlandığına bağlı olacaktır ve diyelim ki,

Base * foo = function(...);dynamic_cast<Derived1>(foo).

Polimorfizm C ++ 'da böyle çalışır.


Ve elbette , şablonların sağladığı gibi bir anyveya bir varianttür kullanabilirsiniz . Böylece, kısıtlama kalmaz.
Deduplicator

Sorunun sorduğu şey tam olarak bu değil, bu da dönüş türünün aynı anda iki tanımlanmış üst sınıfı genişlettiğini belirtmenin bir yolu değildi, yani class Base1{}; class Base2{}; class Derived1 : public Base1, public Base2 {}; class Derived2 : public Base1, public Base2 {}... şimdi ya geri dönüşe izin veren ya Derived1da Derived2hiçbirini döndürmeyen ne tür belirtebiliriz Base1ne Base2doğrudan?
Jules

-1

piton

Çok, çok güçlü yazılmış.

Ancak, bir işlev oluşturulduğunda tür bildirilmez, bu nedenle döndürülen nesneler "belirsiz" olur.

Özel sorunuzda daha iyi bir terim "Polimorfik" olabilir. Bu, Python'daki ortak kullanım durumu, ortak bir arabirim uygulayan varyant türlerini döndürmektir.

def some_function( selector, *args, **kw ):
    if selector == 'this':
        return This( *args, **kw )
    else:
        return That( *args, **kw )

Python kesinlikle yazılı olduğundan, ortaya çıkan nesne bir örneği olur Thisya Thatve (kolaylıkla) zorlanmış ya nesnenin başka bir türe döküm edilemez.


Bu oldukça yanıltıcı; bir nesnenin türü hemen hemen değiştirilemezken, değerler türler arasında kolayca dönüştürülebilir. Örneğin, önemsiz bir şekilde str.
James Youngman

1
@JamesYoungman: Ne? Bu, tüm diller için geçerlidir. Gördüğüm tüm diller sol, sağ ve ortadaki dönüşümleri içermelidir. Yorumunu hiç almadım. Detaylandırabilir misin?
S.Lott

Ne demek istediğini anlamaya çalışıyordum "Python şiddetle yazılmış". Belki de "güçlü yazılan" ile ne demek istediğinizi yanlış anladım. Açıkçası Python, güçlü yazılan dillerle ilişkilendirebileceğim birkaç özelliğe sahiptir. Örneğin, bir işlevin dönüş türünün arayanın değeri kullanmasıyla uyumlu olmadığı programları kabul eder. Örneğin, F () 'nin (z, z, z) döndürdüğü "x, y = F (z)".
James Youngman

Python nesnesinin türü (ciddi sihir olmadan) değiştirilemez. Java ve C ++ ile olduğu gibi "cast" operatörü yoktur. Bu, her nesneyi güçlü bir şekilde yazıyor. Değişken adlarının ve işlev adlarının tür bağlaması yoktur, ancak nesnelerin kendileri güçlü bir şekilde yazılır. Buradaki anahtar kavram, deklarasyonların varlığı değildir. Anahtar kavram, döküm operatörlerinin mevcut olmasıdır. Ayrıca bunun gerçek gibi göründüğünü de unutmayın; Ancak moderatörler buna itiraz edebilir.
S.Lott

1
C ve C ++ döküm işlemleri de işlenen türlerini değiştirmez.
James Youngman
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.