Julia: Proposal: Cabut lalu hapus fungsi perpipaan

Dibuat pada 30 Jan 2017  ·  37Komentar  ·  Sumber: JuliaLang/julia

Usul

Menghentikan penggunaan |> saat ini sebagai pipa fungsi. Artinya, sintaks x |> f akan ditinggalkan demi sintaks panggilan normal f(x) . Setelah periode penghentian, Base.:(|>) tidak akan ditentukan.

Perubahan ini awalnya disarankan oleh tkelman di https://github.com/JuliaLang/julia/issues/16985#issuecomment -227015399.

Ada banyak perdebatan kontroversial mengenai berbagai sintaks untuk pemipaan fungsi (khususnya, lihat #5571), dengan argumen untuk meniru berbagai bahasa. Diskusi itu telah _ad nauseum_ dan saya tidak ingin mengulanginya. BUKAN itu tujuan dari proposal ini.

Alasan

Sejumlah paket yang dipikirkan dengan baik dan terpelihara dengan baik telah mengimplementasikan makro yang menyediakan sintaks perpipaan yang nyaman untuk berbagai kasus penggunaan, baik umum maupun khusus. Contohnya termasuk Lazy.jl , FunctionalData.jl , Pipe.jl , dan ChainMap.jl , antara lain.

StefanKarpinski dan andyferris memberi kami komposisi fungsi arbitrer di #17155, yang dapat melayani tujuan serupa dalam banyak situasi.

Seperti yang diargumentasikan oleh tkelman di #5571, pipa fungsi di Base mundur dari sintaks panggilan yang sudah dikenal; memiliki keduanya dalam bahasa Dasar pada dasarnya mendukung penggunaan 2 sintaks yang berbeda untuk mencapai tujuan yang sama. Meskipun sering ada banyak cara untuk menulis hal yang sama menggunakan solusi di Base, biasanya solusi setidaknya mengikuti model mental yang serupa. Dalam hal ini, sintaks menggunakan model mental yang berlawanan secara harfiah.

Pipa fungsi melanggar prinsip paling tidak mengejutkan dengan menerapkan tindakan setelah objek. Artinya, jika Anda membaca sum(x) Anda langsung tahu saat melihat sum() bahwa Anda akan menjumlahkan nilai dalam argumen. Saat Anda melihat x |> sum , Anda melihat x , lalu tiba-tiba Anda menjumlahkan nilainya. Sedikit jika ada solusi Basis lainnya yang menempatkan tindakan di akhir, yang membuat pemipaan menjadi aneh.

Pemipaan memang memiliki preseden dalam bahasa lain, misalnya %>% Hadley Wickham di R (yang bukan bagian dari basis R), dan terkadang gaya/aliran itu masuk akal. Namun, demi konsistensi di dalam Base Julia, saya mengusulkan agar kami menunda tanggung jawab untuk menyediakan sintaksis perpipaan ke paket, yang dapat mendefinisikan ulang |> atau memberikan makro kenyamanan sesuai keinginan mereka.

Barang Aksi

Jika proposal ini diterima, item tindakannya adalah:

  • [ ] Hapus penggunaan sintaks dalam Base, jika ada
  • [ ] Berikan penghentian formal untuk Base.:(|>) baik dalam 0,6 atau 1,0
  • [ ] Hapus di rilis berikutnya
deprecation design julep

Komentar yang paling membantu

Jika kita menghentikan ini, haruskah kita juga menghentikan * untuk penggabungan string? Itu memiliki masalah yang sama dengan redundan string(a, b) , dan melanggar prinsip yang paling tidak mengejutkan mengingat a dan b bukan angka.

Secara umum, kita mungkin harus menghentikan semua notasi infiks, karena membingungkan untuk memiliki beberapa konvensi pemanggilan seperti *(a, b) vs a * b – kita dapat memangkas 3 sintaks berbeda saat ini menjadi satu dan mendapatkan konsistensi total. Untuk menghindari keburukan, kami mungkin mempertimbangkan untuk memindahkan panggilan fungsi di dalam parens, dan mungkin juga menghilangkan koma yang berlebihan.

Semua 37 komentar

Pemipaan fungsi menyediakan sintaks postfix untuk pemanggilan fungsi, yang nyaman di REPL untuk pembuatan data interaktif dan visualisasi/peringkasan lebih lanjut.

Kasus penggunaan yang saya lihat diketik banyak orang adalah

julia> somecomplicatedthingproducingarray
...

<ARROW UP>

julia> somecomplicatedthingproducingarray |> summarize

di mana fungsi summarize adalah sesuatu seperti plot atau histogram

@jiahao Saya tidak berpendapat bahwa itu tidak berguna, melainkan bahwa kita harus konsisten dalam Base dan membiarkan paket menyediakan hal-hal seperti ini.

ada juga ans untuk penggunaan repl

Dalam proposal ini apakah |> masih akan diurai sebagai operator infix?

@ ajkeller34 : pasti, paket akan bebas melakukan apa pun yang mereka inginkan dengannya (meskipun mereka harus bermain dengan baik satu sama lain dalam hal pembajakan dan koeksistensi tipe), tanpa banyak kendala untuk kompatibel secara semantik dengan yang lama definisi dasar.

Hapus penggunaan sintaks dalam Base, jika ada

Inilah upaya yang sekarang sangat ketinggalan zaman yang saya lakukan untuk melakukan ini: https://github.com/tkelman/julia/commit/212727cdc4aaa3221763580f15d42cfe198bcc1c
Pada saat itu, sebagian besar penggunaan di pangkalan cukup sepele. Beberapa penggunaan tes "menyalurkan benda ini ke fungsi anonim ini" mungkin lebih baik dengan perpipaan, tetapi karena sebagian besar dari mereka menggunakan kembali fungsi anonim yang sama beberapa kali, mungkin ada baiknya memberinya nama dan menyebutnya seperti fungsi normal pada saat itu.

Jika ada yang penasaran, saya punya ChainRecursive.jl sekarang. Saya akan mengumumkan wacana tentang disintegrasi ChainMap.jl dan berbagai anaknya setelah selesai.

Izinkan saya menawarkan beberapa penolakan di sini karena saya memiliki kepentingan pribadi dan kesukaan tertentu pada apa yang dimungkinkan oleh |> .

Saya kedua dengan @jiahao bahwa |> sangat berguna ketika Anda ingin cepat mencoba hal-hal di REPL. Lebih jauh, saya merasa ini juga berguna ketika argumen Anda terlalu besar atau membutuhkan ketenangan (ya, saya mengatakannya) . Dalam kasus contoh tertaut, sebenarnya lebih baik argumennya lebih menonjol daripada fungsi yang dipanggil. sum(x) adalah contoh yang terlalu sederhana, dan memang harus ditulis sebagai sum(x) ). Di Escher.jl semua fungsi yang menambahkan properti ke elemen memiliki metode kari. Ini sangat cocok dengan |> (yang direncanakan, ini juga berfungsi dengan baik dengan map ) dan senang dapat mencoba berbagai hal di akhir baris dan melihat pembaruan UI langsung. Saya tidak perlu menemukan jalan saya ke awal ekspresi dan kebingungan. Untuk digunakan dengan Escher setidaknya, alternatif yang disarankan adalah untuk menetapkan ekspresi besar ke variabel nama yang dibuat-buat seperti padded_box_contents_aligned_right_tomato_background (atau lebih buruk box34 ) dan kemudian memanggil fungsi pada mereka. Berbeda dengan bacaan indah <big UI expression> |> aligncontents(right) |> pad(1em) |> fillcolor("tomato")

Saya tahu bahwa setelah ini saya dapat mendefinisikan |> di dalam Escher dan saya mungkin akan melakukannya, tetapi itu akan membunuh otak saya untuk melihat WARNING: using Escher.|> in module YourPackage conflicts with an existing identifier. Paket hampir pasti akan memberikan arti yang berbeda untuk ini, yang bagi saya sangat menggelisahkan!

StefanKarpinski dan andyferris memberi kami komposisi fungsi arbitrer di #17155, yang dapat melayani tujuan serupa dalam banyak situasi.

Alternatif untuk box |> fill("orange") |> pad(2em) adalah (fill("orange") ∘ pad(2em))(box) sebagai lawan dari box |> fill("orange") ∘ pad(2em) ? Keduanya tampak ortogonal.

Penggunaan penutupan Escher sebagai objek menurut saya seperti mendefinisikan DSL hanya demi menggunakan sintaks ini (yang memiliki batasan serius untuk apa pun yang bukan input tunggal, output tunggal), di mana kemungkinan akan lebih baik dilayani , dan lebih dapat digeneralisasikan, jika menggunakan salah satu dari beberapa makro rantai yang tersedia.

Menghapus definisi Base tentang ini akan memungkinkan orang yang menyukai sintaks ini melakukan hal yang lebih menarik dengannya.

@shashi Saya mengerti poin Anda, tetapi Anda akan bisa mendapatkan perilaku yang sama menggunakan salah satu paket yang saya kutip dalam masalah, bukan? Sebagai contoh, dalam contoh Escher Anda, Anda dapat menggunakan FunctionalData untuk melakukan <strong i="6">@p</strong> vbox(<really big thing>) | pad(2em) atau Malas untuk melakukan @> vbox(...) pad(2em) .

Menghapus definisi Base tentang ini akan memungkinkan orang yang menyukai sintaks ini melakukan hal yang lebih menarik dengannya.

Kecuali itu tidak akan dapat digunakan, karena satu-satunya cara aman untuk menggunakannya adalah Escher.|>(...) atau Lazy.|>(...) .

Secara hipotesis, bagaimana seseorang menggunakan |> sebagai operator infiks jika Anda menggunakan dua paket berbeda yang mendefinisikan dan mengekspornya, dengan asumsi itu tidak didefinisikan dalam Base ?

@kmsquire Itu tergantung pada kasus penggunaan. |> masih akan diuraikan sebagai operator infiks seperti sekarang, hanya saja tidak akan memiliki nilai di Base. Jika Anda menggunakannya dalam makro, tidak masalah bagaimana paket tertentu mendefinisikannya, karena itu hanya menjadi argumen pertama dalam ekspresi panggilan.

Ambil contoh <| , yang diuraikan sebagai operator infiks tetapi tidak memiliki nilai. Meskipun tidak terdefinisi, kita masih memiliki

julia> dump(:(a <| b))
Expr
  head: Symbol call
  args: Array{Any}((3,))
    1: Symbol <|
    2: Symbol a
    3: Symbol b
  typ: Any

Paket dapat mendefinisikan dan mengekspor metode untuk Base.:(<|) yang berarti hal yang berbeda, seperti yang dapat dilakukan dengan + .

Tetapi paket-paket yang menyediakan perpipaan fungsi yang bagus melakukannya di makro, saya berasumsi untuk alasan ini.

FWIW, tidak ada paket chaining yang perlu menggunakan |> selama evaluasi karena selama chaining semuanya di-zip menjadi satu ekspresi. Saya membayangkan jika paket benar-benar mendefinisikan |> itu akan menjadi definisi yang tepat di base. Meskipun mereka mungkin harus menggunakan makro rantai sebagai gantinya. Lihat DataFramesMeta untuk contoh yang baik tentang bagaimana membangun sebuah antarmuka yang bekerja dengan baik dengan rantai.

Jika kita menghentikan ini, haruskah kita juga menghentikan * untuk penggabungan string? Itu memiliki masalah yang sama dengan redundan string(a, b) , dan melanggar prinsip yang paling tidak mengejutkan mengingat a dan b bukan angka.

Secara umum, kita mungkin harus menghentikan semua notasi infiks, karena membingungkan untuk memiliki beberapa konvensi pemanggilan seperti *(a, b) vs a * b – kita dapat memangkas 3 sintaks berbeda saat ini menjadi satu dan mendapatkan konsistensi total. Untuk menghindari keburukan, kami mungkin mempertimbangkan untuk memindahkan panggilan fungsi di dalam parens, dan mungkin juga menghilangkan koma yang berlebihan.

|> masih akan diuraikan sebagai operator infiks seperti sekarang, hanya saja tidak akan memiliki nilai di Base. Jika Anda menggunakannya dalam makro, tidak masalah bagaimana paket tertentu mendefinisikannya

Masih tidak yakin mengapa kita perlu menghapusnya dari Base.

@bramtayl membuat poin yang bagus:

Saya membayangkan jika paket benar-benar mendefinisikan |> itu akan menjadi definisi yang tepat.

Dan masih satu-satunya cara untuk menggunakan lebih dari satu paket yang mendefinisikan ini adalah dengan tidak menggunakannya infix.

Saya tidak mengerti mengapa menghapus definisi di Base diperlukan untuk |> untuk digunakan di dalam makro.

Tidak. Maksud saya adalah bahwa |> dapat digunakan di dalam makro terlepas dari situasi di Base. Hal yang sama berlaku untuk setiap operator yang mem-parsing dengan tepat. Maksud dari proposal ini adalah untuk membuat Base konsisten dalam hal panggilan fungsi, kemudian perilaku perpipaan dapat dicapai melalui paket. Apakah paket menggunakan |> secara khusus tidak masalah; mereka juga bisa menggunakan <| atau operator infix lainnya.

@ararslan benar, bukan itu yang ingin saya tanyakan, saya segera memperbarui komentar saya, maaf.

Bagaimanapun, saya tidak cukup mendapatkan sentimen "Base mandiri dalam hal panggilan fungsi". Sepertinya ini hanya akan mempersulit penggunaan |> dalam konteks non-makro. Saya pribadi percaya |> adalah hal yang berharga untuk dipelajari bagi seorang pemula, meskipun itu mengejutkan. Setidaknya menghemat usaha di REPL. Cukup menyenangkan untuk menyadari kemudian bahwa |> adalah fungsi sama seperti fungsi infiks lainnya dan memperkuat pelajaran bahwa fungsi hanyalah nilai.

Mungkin hanya sesuatu yang formal: Silakan diskusikan/putuskan penghentian dan/atau perubahan sintaks di awal siklus rilis, bukan di akhir. Saat ini semua pengembang utama dan paket yang bertanggung jawab menghabiskan waktu dan energi untuk menyelesaikan 0.6 dan mereka mungkin tidak punya waktu untuk memikirkan ide (baik) lainnya.

"Saya tidak berargumen bahwa itu tidak berguna, tetapi bahwa kita harus konsisten"

Terkadang kegunaan mengalahkan konsistensi? Saya tidak menyadari inkonsistensi, tetapi saya menemukan sintaks |> berguna. Jika dihapus, saya tidak akan merasa mendapatkan sesuatu yang nyata.

Penjelasan untuk suara saya yang tidak suka, jika saya boleh:

Sebagian besar dari apa yang saat ini ada di Base bisa terjadi dalam bentuk paket. Haruskah kita memindahkan kamus ke sebuah paket? Mungkin daftar operasi seperti sort dan shuffle? Operasi koleksi, dll.? Saya yakin telah ada diskusi panjang dan terperinci mengenai apa yang harus dan tidak boleh dimasukkan dalam Base, tetapi saya kira ada tiga alasan beberapa fungsi mungkin disertakan dalam base:
1) Fungsionalitas itu diperlukan untuk mengaktifkan fitur lain di pangkalan.
2) Fungsionalitas itu adalah bagian penting dari bahasa, banyak programmer dan paket Julia akan menggunakannya, dan oleh karena itu diinginkan untuk memiliki satu implementasi/sintaks yang disetujui semua orang, daripada fragmentasi banyak orang yang menggulirkannya sendiri.
3) Menyertakan fungsionalitas itu di basis membuat Julia "mentah" lebih menyenangkan untuk digunakan atau membuatnya merasa lebih berfitur lengkap, yang membantu penginjilan dan adopsi bahasa.

Sesuatu seperti sum mungkin mencapai semua 3 poin, dan saya berpendapat bahwa perpipaan fungsi mencapai poin kedua dan ketiga:

Baik dalam proposal awal (ditulis dengan baik) dan diskusi di utas ini, tema umum adalah keberadaan beberapa paket yang menyediakan fungsionalitas seperti pipa melalui makro: Lazy.jl, Pipe.jl, ChainMap.jl, dll. Keberadaan dari beberapa paket sangat menyarankan bahwa banyak orang di komunitas menganggap perpipaan sebagai fitur yang berguna dan diinginkan, dan kehadiran paket ini di utas diskusi ini menunjukkan bahwa banyak orang di sini memahami dan mendukung penggunaan perpipaan.

Mengingat perpipaan adalah fitur umum dan populer di komunitas Julia dan bahasa lain, bahkan dalam diskusi ini orang tampaknya setuju bahwa itu memiliki banyak kegunaan, terutama di REPL (tempat Julia bersinar), dan sudah ada fragmentasi di ekosistem Julia. ..my read bukan bahwa itu harus dihapus dari Base, melainkan sintaks perpipaan yang tersedia di Base harus ditingkatkan sehingga tidak perlu lagi untuk fragmentasi. Paket yang berbeda menawarkan cara yang berbeda misalnya merencanakan tampaknya baik-baik saja; paket populer yang berbeda yang menawarkan cara berbeda untuk menerapkan fungsi tampaknya cukup menakutkan.

Saya lebih lanjut berpendapat bahwa menghapus perpipaan dari Base tetapi meninggalkan operator infiks agak mengejutkan: di Julia Anda tidak dapat menentukan operator infiks Anda sendiri, tetapi ada operator infiks yang tidak digunakan |> berkeliaran yang dapat Anda definisikan sebagai tolong? Jika itu fungsionalitas yang baik, mengapa tidak memberi kami 10 atau 20 operator infiks yang solid untuk didefinisikan sesuka kami?

Terakhir, saya percaya itu wajar untuk menjaga pemipaan persis karena berbeda dari aplikasi fungsi lainnya. Ini adalah fitur, bukan bug, yang berbeda dari konvensi penerapan fungsi lainnya; perbedaan inilah yang membuatnya bersinar dalam beberapa kasus penggunaan. Dan ada kasus lain di mana (sedikit melambai tangan) kata benda muncul sebelum kata kerja, dan banyak di antaranya adalah gula sintaksis dalam kasus di mana aplikasi fungsi mentah sulit digunakan. Dari atas kepalaku, tugas x = 5 adalah meletakkan kata benda (simbol x ) sebelum kata kerja (mengikat ke nilai). Demikian juga untuk mengakses bidang jenis t.a alih-alih getfield . Dan yang paling penting, pengindeksan array z[5] berbunyi seperti "dari z ambil elemen ke-5" dan umumnya lebih alami daripada getindex(z, 5) .

Jika itu fungsionalitas yang baik, mengapa tidak memberi kami 10 atau 20 operator infiks yang solid untuk didefinisikan sesuka kami?

Mungkin ada lebih dari itu jika seseorang menyertakan semua yang unicode selain yang tidak diklaim ASCII seperti <| , ++ , ...

Tidak membaca seluruh utas -- tetapi hanya ingin mengatakan
bahwa saya suka bisa pipa. Saya akan memilih bermanfaat lebih dari
konsistensi setiap hari.

Saya memiliki preferensi yang sangat ringan untuk menyimpannya, tetapi tidak terlalu peduli selama itu tetap menjadi operator infix. Saya merasa mungkin saya tidak akan menggunakan perpipaan fungsi jika itu mengharuskan mengimpor paket, yang memberi tahu saya bahwa saya tidak terlalu menghargainya.

Meskipun demikian, saya tidak berpikir argumen "prinsip paling tidak mengejutkan" ini menarik, karena membuat beberapa anggapan tentang basis pengguna yang beragam. Untuk penutur asli bahasa subjek-objek-kata kerja, saya kira sebagian besar sintaks Julia melanggar prinsip paling tidak mengejutkan, dan perpipaan fungsi agak nyaman ...

Tidak membaca seluruh utas

😕.

Saya suka bisa pipa

Sekali lagi, saya tidak berargumen bahwa seseorang seharusnya tidak dapat melakukan pemipaan, melainkan fungsionalitasnya dapat dengan mudah didapat di salah satu dari beberapa paket pemipaan yang ada. Menghapus pipa Base memungkinkan paket untuk lebih mudah menentukan semantik pipa mereka sendiri tanpa harus mematuhi atau tetap konsisten dengan apa pun yang disediakan Base.

di Julia Anda tidak dapat menentukan operator infix Anda sendiri

Itu tidak benar; apa pun yang diurai sebagai operator infiks dapat didefinisikan atau didefinisikan ulang. Seperti yang ditunjukkan martinholters, <| dan ++ juga tersedia, antara lain.

Saya agak netral dalam hal ini, tetapi saya akan mendukung sentimen bahwa |> mundur dari sintaks panggilan fungsi normal adalah intinya . Bahkan penggemar terbesar perpipaan tidak meminta (AFAIK) untuk misalnya sin <| x karena itu benar-benar berlebihan dengan sin(x) . |> adalah untuk kasus-kasus di mana lebih mudah bagi mata dan/atau otak untuk memikirkan data yang mengalir dari kiri ke kanan tanpa banyak tanda kurung.

Saya ingin |> menjadi lebih kuat, misalnya allow x |> f(_) + 2g(_) |> h dll. dan agar tidak hanya menjadi operator. Setiap kali seseorang mendefinisikan x |> f berarti sesuatu selain f(x) itu benar-benar membuat saya bingung karena inti dari operator seperti yang kami gunakan adalah bahwa itu adalah sintaks panggilan urutan yang berbeda. Karena kita dapat membebani panggilan, saya tidak dapat melihat alasan yang baik untuk membuat x |> f berarti sesuatu yang lain.

@StefanKarpinski Pipa yang lebih kuat sudah dapat diperoleh menggunakan makro. Lihat misalnya Pipe.jl , yang menyediakan persis sintaks yang Anda gambarkan. Selama |> adalah operator (saya pribadi tidak melihat |> sebagai kasus khusus), makro dapat menggunakan pembatas perpipaan apa pun yang mem-parsing infix, meskipun itu bukan :call . Sebagai contoh, seseorang juga dapat menggunakan @~ untuk mem-pipe (setidaknya pada tulisan ini). Tingkat fleksibilitas itu adalah salah satu keuntungan menggunakan makro di Julia.

Kami dapat menambahkan fungsionalitas Pipe.jl ke bahasa, dan kemudian Anda akan memilikinya tanpa perlu menulis @pipe .

Alasan utama untuk tidak menggunakan |> adalah jika kita ingin mendapatkan kembali sintaks untuk tujuan lain yang lebih disukai orang.

Saya kira saya mencoba berargumen bahwa perpipaan tidak perlu menjadi bagian dari bahasa, itu dapat (dan sudah) hidup dalam sebuah paket.

Tetapi jika tidak ada hal lain yang kita inginkan |> , saya tidak melihat ada salahnya membiarkan definisi (sepele) itu sendiri.

Saya tidak percaya saat ini ada proposal untuk menggunakan kembali |> di Base. Argumen saya untuk tidak mendefinisikannya di Base adalah bahwa ini memberi kita lebih banyak konsistensi tanpa kehilangan fungsionalitas.

Apakah proposal atau implementasi paket "pemipaan yang lebih kuat" akan dibuat lebih sederhana dengan tidak memiliki definisi yang ada untuk dikhawatirkan atau diatasi?

@ararslan "Itu tidak benar; apa pun yang diurai sebagai operator infiks dapat didefinisikan atau didefinisikan ulang."

Dari manual "&& dan || operator", mereka diuraikan tetapi tidak dapat didefinisikan ulang (itu hal yang baik). Saya percaya satu-satunya pengecualian.

Yang disebut "operator logika" && dan || adalah infiks. [hubungan biner unary] "operator" adalah IMHO istilah yang salah untuk mereka karena tidak. Not adalah cara yang mirip dengan logika bitwise & dan | yang memungkinkan overloading (sesuatu yang saya tidak yakin adalah pilihan yang baik).

@PallHaraldsson Itu adalah aliran kontrol, bukan operator dalam arti yang sama seperti & , | , + , dll.

Mari kita coba untuk tetap pada topik di sini jika memungkinkan.

@tkelman Itu poin yang bagus. Saya menduga kita dapat membuat sintaks perpipaan di masa depan kompatibel dengan mundur. Misalnya, jika _ dicadangkan maka |> dapat memiliki arti khusus ketika argumennya berisi _ , dan jika tidak lakukan hal yang sama seperti sekarang.

Ada masalah lain: untuk membuat |> berfungsi untuk objek Anda, apakah Anda mendefinisikan |> atau "operator panggilan fungsi" (yaitu menambahkan metode ke dalamnya)? Mungkin lebih bersih jika |> adalah sintaks bawaan untuk panggilan fungsi, untuk memastikan f(x) dan x |> f selalu sama.

Konsensus di sini sangat jelas bertentangan, jadi saya akan melanjutkan dan menutup masalah ini. Saya menghargai diskusi, semuanya.

Saya tahu masalah ini sudah ditutup. Hanya ingin mengatakan "terima kasih" untuk menjaga operator.

Apakah halaman ini membantu?
0 / 5 - 0 peringkat

Masalah terkait

manor picture manor  ·  3Komentar

TotalVerb picture TotalVerb  ·  3Komentar

omus picture omus  ·  3Komentar

Keno picture Keno  ·  3Komentar

omus picture omus  ·  3Komentar