Durum bilgisi olan bir kütüphanenin üzerinde yan etkisi olmayan arayüz


16

Bir In John Hughes ile mülakat o Erlang ve Haskell bahsediyor, o Erlang durum bilgisi kütüphaneleri kullanma hakkında söylenecek aşağıdaki vardır:

Durum bilgisi olan bir kütüphane kullanmak istersem, genellikle kodumun geri kalanında güvenle kullanabilmem için yan etkisiz bir arayüz oluştururum.

Bununla ne demek istiyor? Bunun nasıl görüneceğine dair bir örnek düşünmeye çalışıyorum, ama hayal gücüm ve / veya bilgim başarısız oluyor.


Devlet varsa, gitmeyecek. İşin püf noktası, bağımlılığı takip edecek bir şey yaratmaktır. Standart Haskell cevabı "monads" veya daha gelişmiş "oklar" dır . Kafanı sarmak biraz zor ve ben gerçekten yapmadım, bu yüzden başka biri onları açıklamaya çalışmak zorunda kalacaktı.
Jan Hudec

Yanıtlar:


12

(Erlang'ı tanımıyorum ve Haskell yazamıyorum, ama yine de cevaplayabileceğimi düşünüyorum)

Bu röportajda rastgele sayı üretme kütüphanesi örneği verilmiştir. İşte olası durum bilgisi olan bir arayüz:

# create a new RNG
var rng = RNG(seed)

# every time we call the next(ceil) method, we get a new random number
print rng.next(10)
print rng.next(10)
print rng.next(10)

Çıktı olabilir 5 2 7. Değişmezliği seven biri için bu yanlıştır! Olmalı 5 5 5, çünkü yöntemi aynı nesne üzerinde çağırdık.

Vatansız bir arayüz ne olurdu? Rastgele sayılar dizisini next, kafayı gerçekten ele aldığı tembel bir değerlendirme listesi olarak görüntüleyebiliriz :

let rng = RNG(seed)
let n : rng = rng in
  print n
  let n : rng = rng in
    print n
    let n : rng in
      print n

Böyle bir arayüzle her zaman önceki bir duruma dönebiliriz. Kodunuzun iki parçası aynı RNG'ye başvuruyorsa, aslında aynı sayı dizisini alırlar. İşlevsel bir zihniyette bu oldukça arzu edilir.

Bunu durumsal bir dilde uygulamak o kadar da karmaşık değildir. Örneğin:

import scala.util.Random
import scala.collection.immutable.LinearSeq

class StatelessRNG (private val statefulRNG: Random, bound: Int) extends LinearSeq[Int] {
  private lazy val next = (statefulRNG.nextInt(bound), new StatelessRNG(statefulRNG, bound))

  // the rest is just there to satisfy the LinearSeq trait
  override def head = next._1
  override def tail = next._2
  override def isEmpty = false
  override def apply(i: Int): Int = throw new UnsupportedOperationException()
  override def length = throw new UnsupportedOperationException()
}

// print out three nums
val rng = new StatelessRNG(new Random(), 10)
rng.take(3) foreach (n => println(n))

Bir liste gibi hissetmek için biraz sözdizimsel şeker eklediğinizde, bu aslında oldukça güzel.


1
İlk örneğinize gelince: her seferinde farklı değerler üreten rnd.next (10), bir fonksiyonun tanımıyla olduğu kadar değişmezlikle de ilgili değildir: fonksiyonlar 1'e 1 olmalıdır. (+1 olsa da, iyi şeyler)
Steven Evers

Teşekkürler! Bu gerçekten hoş, göze çarpan bir açıklama ve örnekti.
beta

1

Burada kilit bir kavram, dışsal değişebilir durumdur . Harici değişken durumu olmayan bir kütüphane, yan etkileri olmayan kütüphanedir. Böyle bir kütüphanedeki her fonksiyon sadece kütüphaneye aktarılan argümanlara bağlıdır.

  • İşleviniz, kendisine verilmeyen herhangi bir kaynağa erişirse (yani parametre olarak), harici duruma bağlıdır .
  • Eğer fonksiyonunuz arayana geri dönmeyen (ve onu yok etmeyen) bir şey yaratırsa, o zaman fonksiyonunuz harici durum yaratır.
  • Yukarıdaki harici durum farklı zamanlarda farklı değerlere sahip olabiliyorsa, değiştirilebilir .

Kullandığım kullanışlı turnusol testleri:

  • A işlevinin B işlevinden önce çalıştırılması gerekiyorsa A, B'nin bağlı olduğu harici bir durum oluşturur.
  • yazdığım bir fonksiyon hafızaya alınamazsa, harici değiştirilebilir duruma bağlıdır. (Bellek baskısı nedeniyle not iyi bir fikir olmayabilir, ancak yine de mümkün olmalıdır)
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.