मैं समझता हूं कि आम तौर पर यह तय करने के बारे में बहुत कुछ कहना है कि कौन सा मॉडल प्रभाव के रूप में मॉडल करना चाहता है यह चर्चा आईओ पर अध्याय पर स्कैला में कार्यात्मक प्रोग्रामिंग में पेश की गई है।

फिर भी, मैंने अध्याय समाप्त नहीं किया है, मैं इसे Cats IO के साथ एक साथ लेने से पहले इसे अंत तक ब्राउज़ कर रहा था।

इस बीच, मेरे पास कुछ कोड के लिए एक स्थिति है जो मुझे जल्द ही काम पर देने की आवश्यकता है। यह जावा लाइब्रेरी पर निर्भर करता है जो सिर्फ उत्परिवर्तन के बारे में है। वह पुस्तकालय बहुत समय पहले शुरू किया गया था और विरासत के कारण मैं उन्हें बदलते नहीं देखता।

वैसे भी, लंबी कहानी छोटी। क्या वास्तव में किसी भी उत्परिवर्तित कार्य को IO के रूप में एक उत्परिवर्तित जावा पुस्तकालय को समाहित करने का एक व्यवहार्य तरीका है?

संपादित करें1 (अनुरोध पर मैं एक स्निपेट जोड़ता हूं)

एक मॉडल में तैयार होने के लिए, एक नया मॉडल बनाने के बजाय मॉडल को बदलें। उदाहरण के लिए, मैं जेना को ग्रेमलिन से तुलना करूंगा, उदाहरण के लिए, ग्राफ डेटा पर एक कार्यात्मक पुस्तकालय।

def loadModel(paths: String*): Model =
    paths.foldLeft(ModelFactory.createOntologyModel(new OntModelSpec(OntModelSpec.OWL_MEM)).asInstanceOf[Model]) {
      case (model, path) ⇒
        val input = getClass.getClassLoader.getResourceAsStream(path)
        val lang  = RDFLanguages.filenameToLang(path).getName
        model.read(input, "", lang)
    }

वह मेरा स्कैला कोड था, लेकिन वेबसाइट में दस्तावेज के रूप में जावा एपीआई इस तरह दिखता है।

// create the resource
Resource r = model.createResource();

// add the property
r.addProperty(RDFS.label, model.createLiteral("chat", "en"))
 .addProperty(RDFS.label, model.createLiteral("chat", "fr"))
 .addProperty(RDFS.label, model.createLiteral("<em>chat</em>", true));

// write out the Model
model.write(system.out);
// create a bag
Bag smiths = model.createBag();

// select all the resources with a VCARD.FN property
// whose value ends with "Smith"
StmtIterator iter = model.listStatements(
    new SimpleSelector(null, VCARD.FN, (RDFNode) null) {
        public boolean selects(Statement s) {
                return s.getString().endsWith("Smith");
        }
    });
// add the Smith's to the bag
while (iter.hasNext()) {
    smiths.add(iter.nextStatement().getSubject());
}
0
MaatDeamon 12 फरवरी 2021, 16:28
सुनिश्चित नहीं है कि आपका क्या मतलब है " वास्तव में किसी भी उत्परिवर्तित फ़ंक्शन को IO के रूप में मॉडलिंग कर रहा है जो एक उत्परिवर्तित जावा पुस्तकालय को समाहित करने का एक व्यवहार्य तरीका है"। जैसे कि आप सुनिश्चित नहीं हैं कि क्या आप पूछ रहे हैं कि क्या यह करना सही है, या यदि यह वास्तव में आवश्यक है या यदि कोई अन्य तरीका है। साथ ही, यह दिखाने के लिए कि आप उस पुस्तकालय का उपयोग कैसे करते हैं, एक छोटा सा स्निपेट प्रदान करना अच्छा हो सकता है।
 – 
Luis Miguel Mejía Suárez
12 फरवरी 2021, 16:30
मेरा मतलब था कि सबसे लोकप्रिय विकल्प क्या हैं जो अनुभव स्कैला डेवलपर आमतौर पर चुनते हैं। और क्या मैं कुछ ऐसा सोच रहा हूं जो वास्तव में कोई नहीं करता है क्योंकि यह ज्ञात है कि पीछे हटना है
 – 
MaatDeamon
12 फरवरी 2021, 16:44
फिर से, पुस्तकालय कैसे काम करता है और आप इसका उपयोग कैसे करेंगे, इस बारे में एक छोटे से सुराग के बिना बताना मुश्किल है।
 – 
Luis Miguel Mejía Suárez
12 फरवरी 2021, 16:50
वास्तविक जावा एपीआई स्निपेट जोड़ा गया, क्योंकि मेरा कोड पहले से ही कुछ स्कैला शिनिनिगन कर रहा था :)
 – 
MaatDeamon
12 फरवरी 2021, 16:56
और आपको उस एपीआई के साथ क्या करने की ज़रूरत है? बस मॉडल पढ़ें या मॉडल का उपयोग करें?
 – 
Luis Miguel Mejía Suárez
12 फरवरी 2021, 17:02

1 उत्तर

सबसे बढ़िया उत्तर

तो, इस समस्या के तीन समाधान हैं।

1. सरल और गंदा

यदि अशुद्ध एपीआई का पूरा उपयोग कोड आधार के एकल/छोटे हिस्से में निहित है, तो आप बस "धोखा" कर सकते हैं और कुछ ऐसा कर सकते हैं:

def useBadJavaAPI(args): IO[Foo] = IO {
  // Everything inside this block can be imperative and mutable.
}

मैंने कहा "धोखा" क्योंकि IO का विचार रचना है, और एक बड़ा IO हिस्सा वास्तव में रचना नहीं है। लेकिन, कभी-कभी आप केवल उस विरासत वाले हिस्से को समेटना चाहते हैं और इसकी परवाह नहीं करते हैं।

2. रचना की ओर।

मूल रूप से, ऊपर जैसा ही है लेकिन बीच में कुछ flatMaps छोड़ रहा है:

// Instead of:
def useBadJavaAPI(args): IO[Foo] = IO {
  val a = createMutableThing()
  mutableThing.add(args)
  val b = a.bar()
  b.computeFoo()
}

// You do something like this:
def useBadJavaAPI(args): IO[Foo] =
  for {
    a <- IO(createMutableThing())
    _ <- IO(mutableThing.add(args))
    b <- IO(a.bar())
    result <- IO(b.computeFoo())
  } yield result

ऐसा करने के दो कारण हैं:

  1. क्योंकि अनिवार्य/परिवर्तनीय एपीआई एक विधि/वर्ग में नहीं बल्कि उनमें से कुछ में निहित है। और IO में छोटे कदमों का इनकैप्सुलेशन आपको इसके बारे में तर्क करने में मदद कर रहा है।
  2. क्योंकि आप कोड को धीरे-धीरे कुछ बेहतर करने के लिए माइग्रेट करना चाहते हैं।
  3. क्योंकि आप अपने साथ बेहतर महसूस करना चाहते हैं :p

3. इसे शुद्ध इंटरफ़ेस में लपेटें

यह मूल रूप से वही है जो कई तृतीय पक्ष पुस्तकालय (उदा. Doobie, fs2-blobstore, नियोटाइप) करते हैं। एक शुद्ध इंटरफ़ेस पर जावा लाइब्रेरी लपेटना।

ध्यान दें कि इस तरह, जितना काम करना है, वह पिछले दो समाधानों की तुलना में बहुत अधिक है। जैसे, यह इसके लायक है यदि परिवर्तनशील एपीआई आपके कोडबेस के कई स्थानों पर "संक्रमित" है, या कई परियोजनाओं में बदतर है; यदि ऐसा है तो ऐसा करना समझ में आता है और प्रकाशित करना एक स्वतंत्र मॉड्यूल के रूप में है।
(उस मॉड्यूल को ओपन-सोर्स लाइब्रेरी के रूप में प्रकाशित करना भी उचित हो सकता है, आप अंत में अन्य लोगों की मदद कर सकते हैं और अन्य लोगों से भी सहायता प्राप्त कर सकते हैं)

चूंकि यह एक बड़ा कार्य है, इसलिए आपको जो कुछ करना होगा उसका पूरा उत्तर देना आसान नहीं है, यह देखने में मदद मिल सकती है कि उन पुस्तकालयों को कैसे कार्यान्वित किया जाता है और या तो यहां या गटर चैनलों में अधिक प्रश्न पूछें।

लेकिन, मैं आपको एक त्वरित स्निपेट दे सकता हूं कि यह कैसा दिखेगा:

// First define a pure interface of the operations you want to provide
trait PureModel[F[_]] { // You may forget about the abstract F and just use IO instead.
  def op1: F[Int]
  def op2(data: List[String]): F[Unit]
}

// Then in the companion object you define factories.
object PureModel {
  // If the underlying java object has a close or release action,
  // use a Resource[F, PureModel[F]] instead.
  def apply[F[_]](args)(implicit F: Sync[F]): F[PureModel[F]] = ???
}

अब, कार्यान्वयन कैसे बनाया जाए यह मुश्किल हिस्सा है। हो सकता है कि आप परिवर्तनशील अवस्था को आरंभ करने के लिए Sync जैसी किसी चीज़ का उपयोग कर सकें।

def apply[F[_]](args)(implicit F: Sync[F]): F[PureModel[F]] =
  F.delay(createMutableState()).map { mutableThing =>
    new PureModel[F] {
      override def op1: F[Int] = F.delay(mutableThing.foo())
      override def op2(data: List[String]): F[Unit] = F.delay(mutableThing.bar(data))
    }
  }
4
Luis Miguel Mejía Suárez 6 मार्च 2021, 16:47