Giao diện không có hiệu ứng phụ trên đầu thư viện


16

Trong một cuộc phỏng vấn với John Hughes nơi anh nói về Erlang và Haskell, anh có những điều sau đây để nói về việc sử dụng các thư viện có trạng thái trong Erlang:

Nếu tôi muốn sử dụng một thư viện có trạng thái, tôi thường xây dựng giao diện không có hiệu ứng phụ bên trên nó để tôi có thể sử dụng nó một cách an toàn trong phần còn lại của mã.

ông không có ý nghĩa gì bởi điều này? Tôi đang cố gắng nghĩ về một ví dụ về việc nó sẽ trông như thế nào, nhưng trí tưởng tượng và / hoặc kiến ​​thức của tôi đang làm tôi thất bại.


Chà, nếu nhà nước tồn tại, nó sẽ không biến mất. Bí quyết là tạo ra thứ gì đó sẽ theo dõi sự phụ thuộc. Câu trả lời Haskell tiêu chuẩn là "đơn nguyên" hoặc "mũi tên" nâng cao hơn . Họ hơi khó khăn để quấn đầu bạn và tôi chưa bao giờ thực sự làm vậy, vì vậy người khác sẽ phải cố gắng giải thích chúng.
Jan Hudec

Câu trả lời:


12

(Tôi không biết Erlang và tôi không thể viết Haskell, nhưng tôi nghĩ tôi vẫn có thể trả lời)

Vâng, trong cuộc phỏng vấn đó, ví dụ về một thư viện tạo số ngẫu nhiên được đưa ra. Đây là một giao diện trạng thái có thể:

# 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)

Đầu ra có thể 5 2 7. Đối với một người thích sự bất biến, điều này hoàn toàn sai! Nó nên như vậy 5 5 5, bởi vì chúng ta đã gọi phương thức trên cùng một đối tượng.

Vì vậy, một giao diện phi trạng thái sẽ là gì? Chúng ta có thể xem chuỗi các số ngẫu nhiên như một danh sách được đánh giá một cách lười biếng, trong đó nextthực sự lấy ra đầu:

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

Với giao diện như vậy, chúng ta luôn có thể trở lại trạng thái trước đó. Nếu hai đoạn mã của bạn tham chiếu đến cùng một RNG, chúng thực sự sẽ có cùng một chuỗi số. Trong một tư duy chức năng, điều này là rất mong muốn.

Thực hiện điều này trong một ngôn ngữ nhà nước không phải là phức tạp. Ví dụ:

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))

Một khi bạn thêm một chút đường cú pháp để nó cảm thấy giống như một danh sách, điều này thực sự khá hay.


1
Đối với ví dụ đầu tiên của bạn: rnd.next (10) tạo ra các giá trị khác nhau mỗi lần không phải làm với tính bất biến nhiều như nó phải làm với định nghĩa của hàm: các hàm phải là 1 trên 1. (Mặc dù +1, công cụ tốt)
Steven Evers

Cảm ơn! Đó là một lời giải thích và ví dụ thực sự tốt đẹp.
beta

1

Một khái niệm quan trọng ở đây là trạng thái đột biến bên ngoài . Một thư viện không có trạng thái đột biến bên ngoài, là một thư viện không có tác dụng phụ. Mỗi chức năng trong một thư viện như vậy chỉ phụ thuộc vào các đối số được truyền vào nó.

  • Nếu chức năng của bạn truy cập bất kỳ tài nguyên nào không được tạo bởi nó, được cung cấp cho nó (tức là dưới dạng tham số), thì nó phụ thuộc vào trạng thái bên ngoài .
  • Nếu chức năng của bạn tạo ra thứ gì đó mà nó không chuyển lại cho người gọi (và không phá hủy nó) thì chức năng của bạn đang tạo trạng thái bên ngoài.
  • Khi trạng thái bên ngoài từ phía trên có thể có các giá trị khác nhau tại các thời điểm khác nhau, thì nó có thể thay đổi .

Các xét nghiệm litmus tiện dụng mà tôi sử dụng:

  • nếu hàm A cần được chạy trước hàm B thì A tạo trạng thái bên ngoài mà B phụ thuộc vào.
  • nếu một chức năng tôi đang viết không thể được ghi nhớ, thì nó phụ thuộc vào trạng thái biến đổi bên ngoài. (Việc ghi nhớ có thể không phải là một ý tưởng tốt vì áp lực bộ nhớ, nhưng nó vẫn có thể xảy ra)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.