Durum bilgisi olan nesnelerin oluşturulması bir etki türüyle modellenmeli mi?


9

Scala gibi işlevsel bir ortam kullanırken ve cats-effectdurum bilgisi olan nesnelerin inşası bir efekt türüyle modellenmeli mi?

// not a value/case class
class Service(s: name)

def withoutEffect(name: String): Service =
  new Service(name)

def withEffect[F: Sync](name: String): F[Service] =
  F.delay {
    new Service(name)
  }

İnşaat hata vermez, bu yüzden daha zayıf bir tip sınıfı kullanabiliriz Apply.

// never throws
def withWeakEffect[F: Applicative](name: String): F[Service] =
  new Service(name).pure[F]

Tüm bunların saf ve deterministik olduğunu düşünüyorum. Ortaya çıkan örnek her seferinde farklı olduğu için referans olarak saydam değildir. Bir efekt türü kullanmak için iyi bir zaman mı? Yoksa burada farklı bir işlevsel model olabilir mi?


2
Evet, değişebilir durumun yaratılması bir yan etkidir. Bu nedenle, a içinde olmalı delayve bir F [Hizmet] döndürmelidir . Örnek olarak, IO'dakistart yönteme bakın , düz fiber yerine bir IO [Fiber [IO,?]] Döndürür.
Luis Miguel Mejía Suárez

1
Bu sorunun tam cevabı için lütfen bu & bu .
Luis Miguel Mejía Suárez

Yanıtlar:


3

Durum bilgisi olan nesnelerin oluşturulması bir etki türüyle modellenmeli mi?

Zaten bir efekt sistemi kullanıyorsanız, büyük olasılıkla Refdeğiştirilebilir durumu güvenli bir şekilde kapsüllemek için bir türü vardır .

Ben de diyorum ki: durum bilgisi olan nesneleri modelleyinRef . Bunların oluşturulması (ve bunlara erişim) zaten bir etki olduğundan, bu, hizmeti otomatik olarak da etkili hale getirecektir.

Bu, orijinal sorunuzun düzgün bir şekilde yan adımları.

Dahili değişken durumu düzenli olarak manuel olarak yönetmek varistiyorsanız, bu duruma dokunan tüm işlemlerin sıkıcı ve hataya eğilimli etkiler olarak kabul edildiğinden (ve büyük olasılıkla iş parçacığı açısından güvenli hale getirildiğinden) emin olmalısınız. Bu yapılabilir ve @ atl'ın cevabına katılıyorum, durumsal nesnenin yaratılmasını kesinlikle etkilemeniz gerekmediği (referans bütünlüğünün kaybı ile yaşayabildiğiniz sürece), ama neden kendinizi beladan kurtarmayın ve kucaklamayın Etki sisteminizin araçları tüm yol boyunca?


Tüm bunların saf ve deterministik olduğunu düşünüyorum. Ortaya çıkan örnek her seferinde farklı olduğu için referans olarak saydam değildir. Bir efekt türü kullanmak için iyi bir zaman mı?

Sorunuz şu şekilde yeniden ifade edilebilirse:

Ek faydalar ("zayıf tip sınıfı" kullanarak doğru çalışan bir uygulamanın üstünde) ve durum için de bir etki türü (halihazırda devlet erişimi ve mutasyonu için kullanılmalıdır) kullanılmasını haklı gösterecek kadar yeterli mi? yaratma?

sonra: Evet, kesinlikle .

Bunun neden faydalı olduğuna dair bir örnek vermek için:

Hizmet yaratma bir etki yaratmasa da aşağıdakiler işe yarar:

val service = makeService(name)
for {
  _ <- service.doX()
  _ <- service.doY()
} yield Ack.Done

Ancak bunu aşağıdaki gibi yeniden düzenlerseniz, derleme zamanı hatası almayacaksınız, ancak davranışı değiştirmiş olacaksınız ve büyük olasılıkla bir hata eklediniz. makeServiceEtkili olduğunu beyan etmiş olsaydınız, yeniden düzenleme işlemi tür denetlemez ve derleyici tarafından reddedilmezdi.

for {
  _ <- makeService(name).doX()
  _ <- makeService(name).doY()
} yield Ack.Done

Yöntemin adlandırılmasını makeService(ve bir parametreyle de), yöntemin ne yaptığını ve yeniden düzenleme işleminin yapmak için güvenli bir şey olmadığını, ancak "yerel akıl yürütme" isimlendirme kuralları ve makeServicebunu anlamak için: Mekanizmayı mekanik olarak karıştırılamayan (tekilleştirilmemiş, tembel hale getirilmiş, istekli, ölü kod kaldırılmış, paralelleştirilmiş, gecikmeli, önbelleğe alınmış, önbellekten temizlenmiş vb.) yani "saf" değildir) etkili olarak yazılmalıdır.


2

Durum bilgisi olan hizmet bu durumda neyi ifade eder?

Bir nesne inşa edildiğinde bir yan etki yürüteceği anlamına mı geliyor? Bunun için, uygulamanız başlarken yan etkiyi çalıştıran bir yönteme sahip olmak daha iyi bir fikir olacaktır. İnşaat sırasında çalıştırmak yerine.

Ya da belki de hizmet içinde değişebilir bir duruma sahip olduğunu söylüyorsunuz? Dahili değişken durum maruz kalmadıkça, iyi olmalıdır. Hizmetle iletişim kurmak için saf (referans olarak şeffaf) bir yöntem sağlamanız yeterlidir.

İkinci noktamı genişletmek için:

Diyelim ki bir bellekte db inşa ediyoruz.

class InMemoryDB(private val hashMap: ConcurrentHashMap[String, String]) {
  def getId(s: String): IO[String] = ???
  def setId(s: String): IO[Unit] = ???
}

object InMemoryDB {
  def apply(hashMap: ConcurrentHashMap[String, String]) = new InMemoryDB(hashMap)
}

IMO, bunun etkili olması gerekmez, çünkü bir ağ çağrısı yaparsanız aynı şey oluyor. Bununla birlikte, bu sınıfın yalnızca bir örneğinin olduğundan emin olmanız gerekir.

Eğer Refkediler-efektinden kullanıyorsanız , normalde yaptığım şey flatMapgiriş noktasındaki ref'ye, yani sınıfınızın etkili olması gerekmez.

object Effectful extends IOApp {

  class InMemoryDB(storage: Ref[IO, Map[String, String]]) {
    def getId(s: String): IO[String] = ???
    def setId(s: String): IO[Unit] = ???
  }

  override def run(args: List[String]): IO[ExitCode] = {
    for {
      storage <- Ref.of[IO, Map[String, String]](Map.empty[String, String])
      _ = app(storage)
    } yield ExitCode.Success
  }

  def app(storage: Ref[IO, Map[String, String]]): InMemoryDB = {
    new InMemoryDB(storage)
  }
}

OTOH, durum bilgisi olan bir nesneye (diyelim ki birden fazla eşzamanlılık ilkeli olan) bağımlı bir hizmet veya kitaplık yazıyorsanız ve kullanıcılarınızın neyi başlatacaklarını ummalarını istemiyorsanız.

Sonra, evet, bir efektle sarılmalıdır. Her Resource[F, MyStatefulService]şeyin düzgün bir şekilde kapatıldığından emin olmak için gibi bir şey kullanabilirsiniz . Ya da sadece F[MyStatefulService]kapanacak bir şey yoksa.


"Hizmetle iletişim kurmak için saf bir yöntem sağlamanız yeterlidir" Ya da tam tersi: Tamamen içsel durumun ilk inşasının bir etkisi olması gerekmez, ancak hizmetteki bu değişken durumla etkileşime giren herhangi bir işlem herhangi bir şekilde etkili olarak işaretlenmesi gerekir (gibi kazaları önlemek için val neverRunningThisButStillMessingUpState = Task.pure(service.changeStateThinkingThisIsPure()).repeat(5))
Thilo

Veya diğer taraftan geliyorsa: Bu hizmet oluşturmayı etkili ya da etkili kılmazsanız gerçekten önemli değil. Ancak hangi yoldan giderseniz gidin, bu hizmetle herhangi bir şekilde etkileşim kurmak etkili olmalıdır (çünkü içinde bu etkileşimlerden etkilenecek değişebilir bir durum taşır).
Thilo

1
@thilo Evet, haklısın. Demek istediğim pure, referans olarak şeffaf olması gerektiğidir. örneğin Future ile bir örnek düşünün. val x = Future {... }ve def x = Future { ... }farklı bir şey ifade ediyor. (Bu, kodunuzu yeniden düzenlerken sizi ısırabilir) Ancak, kediler-etkisi, monix veya zio için durum böyle değildir.
atl
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.