স্কালার প্রভাব কোথায়?


398

স্কেলে নতুন আগতদের কাছে একটি অন্তর্নিহিত প্রশ্নটি মনে হয়: সংকলক কোথায় বোঝায়? আমি অন্তর্নিহিত বলতে চাইছি কারণ প্রশ্নটি কখনই পুরোপুরি গঠন হয়ে যায় বলে মনে হয় না, কারণ এটির জন্য শব্দ নেই। :-) উদাহরণস্বরূপ, integralনীচের মানগুলি কোথা থেকে আসে?

scala> import scala.math._
import scala.math._

scala> def foo[T](t: T)(implicit integral: Integral[T]) {println(integral)}
foo: [T](t: T)(implicit integral: scala.math.Integral[T])Unit

scala> foo(0)
scala.math.Numeric$IntIsIntegral$@3dbea611

scala> foo(0L)
scala.math.Numeric$LongIsIntegral$@48c610af

প্রথম প্রশ্নের উত্তর শিখার সিদ্ধান্ত নেওয়ার বিষয়ে তাদের অনুসরণকারী আরেকটি প্রশ্ন হ'ল সংকলক আপাত অস্পষ্টতার কিছু পরিস্থিতিতে (তবে যে কোনওভাবেই সংকলন করবে) কোনটি ব্যবহার করতে হবে তা কীভাবে বেছে নেবে?

উদাহরণস্বরূপ, scala.Predefদুটি রূপান্তর সংজ্ঞায়িত করে String: একটিতে WrappedStringএবং অন্যটিতে StringOps। উভয় শ্রেণি, যদিও, প্রচুর পদ্ধতি ভাগ করে নেয়, সুতরাং স্কালা কেন, কল করার সময়, অস্পষ্টতার অভিযোগ করবে না map?

দ্রষ্টব্য: আরও সাধারণ পদ্ধতিতে সমস্যাটি বলার আশায় এই প্রশ্নটি এই অন্যান্য প্রশ্নের দ্বারা অনুপ্রাণিত হয়েছিল । উদাহরণটি সেখান থেকে অনুলিপি করা হয়েছিল, কারণ এটি উত্তরে উল্লেখ করা হয়েছে।

উত্তর:


554

ছদ্মবেশের প্রকারগুলি

স্কালায় প্রভাবগুলি হয় এমন কোনও মানকে বোঝায় যা "স্বয়ংক্রিয়ভাবে" উত্তীর্ণ হতে পারে, তাই কথা বলতে বা এক ধরণের থেকে অন্য ধরণের রূপান্তর যা স্বয়ংক্রিয়ভাবে তৈরি হয়।

অন্তর্নিহিত রূপান্তর

পরের ধরণের সম্পর্কে খুব সংক্ষেপে কথা বললে, যদি কোনও শ্রেণীর mকোনও বস্তুর উপর কোনও পদ্ধতি কল করে এবং সেই শ্রেণিটি পদ্ধতিটিকে সমর্থন করে না , তবে স্কালা কোনও কিছুকে সমর্থন করে এমন একটি রূপান্তর রূপান্তর খুঁজবে । একটি সহজ উদাহরণ পদ্ধতি হবে :oCmCmmapString

"abc".map(_.toInt)

Stringপদ্ধতি অবলম্বন পাওয়া যায়নি map, কিন্তু StringOpsনা, এবং সেখান থেকে একটি অন্তর্নিহিত রূপান্তর এর Stringথেকে StringOpsউপলব্ধ (দেখুন implicit def augmentStringউপর Predef)।

অন্তর্নিহিত পরামিতি

অন্য ধরণের ইমপ্লিট প্যারামিটার । এগুলি অন্য কোনও প্যারামিটারের মতো পদ্ধতি কলগুলিতে পাস করা হয় তবে সংকলক সেগুলি স্বয়ংক্রিয়ভাবে পূরণ করার চেষ্টা করে। এটি না পারলে অভিযোগ করবে। কেউ এই প্যারামিটারগুলি স্পষ্টভাবে পাস করতে পারে, যা এটি কীভাবে ব্যবহার করে breakOut, উদাহরণস্বরূপ ( breakOutকোনও দিন আপনি কোনও চ্যালেঞ্জের জন্য অনুভব করছেন সে সম্পর্কে প্রশ্ন দেখুন )।

এক্ষেত্রে, কোনও ব্যক্তিকে প্রয়োজনের ঘোষণা দিতে হবে, যেমন fooপদ্ধতি ঘোষণার মতো:

def foo[T](t: T)(implicit integral: Integral[T]) {println(integral)}

সীমা দেখুন

এখানে একটি পরিস্থিতি রয়েছে যেখানে অন্তর্নিহিত হ'ল অন্তর্নিহিত রূপান্তর এবং অন্তর্নিহিত পরামিতি উভয়ই। উদাহরণ স্বরূপ:

def getIndex[T, CC](seq: CC, value: T)(implicit conv: CC => Seq[T]) = seq.indexOf(value)

getIndex("abc", 'a')

পদ্ধতিটি getIndexকোনও অবজেক্ট গ্রহণ করতে পারে, যতক্ষণ না তার শ্রেণি থেকে কোনও অন্তর্নিহিত রূপান্তর উপলব্ধ থাকে Seq[T]। যে কারণে, আমি পাস করতে পারেন Stringথেকে getIndex, এবং এটি কাজ করবে।

পর্দার আড়ালে, সংকলকটিতে পরিবর্তিত seq.IndexOf(value)হয় conv(seq).indexOf(value)

এটি এতটাই কার্যকর যে এগুলি লেখার জন্য সিনট্যাকটিক চিনি রয়েছে। এই সিনট্যাকটিক চিনি ব্যবহার করে, এটির getIndexমতো সংজ্ঞা দেওয়া যেতে পারে:

def getIndex[T, CC <% Seq[T]](seq: CC, value: T) = seq.indexOf(value)

এই অন্বিত চিনি হিসেবে বর্ণনা করা হয়েছে দৃশ্য আবদ্ধ , এর একটি সদৃশ উপরের আবদ্ধ ( CC <: Seq[Int]ক) বা নিম্ন মুখী ( T >: Null)।

প্রসঙ্গ সীমা

অন্তর্নিহিত পরামিতিগুলির আর একটি সাধারণ প্যাটার্ন হ'ল ধরণ শ্রেণির ধরণ । এই প্যাটার্নটি ক্লাসগুলিতে সাধারণ ইন্টারফেসের বিধানকে সক্ষম করে যা তাদের ঘোষণা করে না। এটি উভয়ই একটি সেতুর প্যাটার্ন হিসাবে পরিবেশন করতে পারে - উদ্বেগের পৃথকীকরণ - এবং অ্যাডাপ্টারের নিদর্শন হিসাবে।

আপনি যে Integralশ্রেণীর কথা উল্লেখ করেছেন তা হ'ল ধরণের শ্রেণিবদ্ধের নমুনা। স্কালার স্ট্যান্ডার্ড লাইব্রেরির আর একটি উদাহরণ Ordering। এখানে একটি লাইব্রেরি রয়েছে যা এই প্যাটার্নটির ভারী ব্যবহার করে যার নাম স্কালাজ।

এটি এর ব্যবহারের একটি উদাহরণ:

def sum[T](list: List[T])(implicit integral: Integral[T]): T = {
    import integral._   // get the implicits in question into scope
    list.foldLeft(integral.zero)(_ + _)
}

এর জন্য সিনট্যাকটিক চিনিও রয়েছে, যাকে প্রসঙ্গবদ্ধ বাউন্ড বলা হয়, যা অন্তর্নিহিতকে উল্লেখ করার প্রয়োজনে কম দরকারী করে তোলে। এই পদ্ধতির একটি সরল রূপান্তর এইরকম দেখাচ্ছে:

def sum[T : Integral](list: List[T]): T = {
    val integral = implicitly[Integral[T]]
    import integral._   // get the implicits in question into scope
    list.foldLeft(integral.zero)(_ + _)
}

প্রসঙ্গ সীমা আরো উপযোগী যখন আপনি শুধু প্রয়োজন হয় পাস তাদের অন্যান্য পদ্ধতি যে তাদের ব্যবহার করতে। উদাহরণস্বরূপ, পদ্ধতি sortedউপর Seqএকটি অন্তর্নিহিত প্রয়োজন Ordering। একটি পদ্ধতি তৈরি করতে reverseSort, কেউ লিখতে পারেন:

def reverseSort[T : Ordering](seq: Seq[T]) = seq.sorted.reverse

যেহেতু Ordering[T]অন্তর্নিহিতভাবে প্রেরণ করা হয়েছিল reverseSort, এটি পরে এটি সম্পূর্ণরূপে প্রেরণ করতে পারে sorted

ইমপ্লিটস কোথা থেকে আসে?

যখন সংকলক কোনও অন্তর্নিহিতের প্রয়োজনীয়তাটি দেখেন, হয় আপনি এমন কোনও পদ্ধতির জন্য যা যা বস্তুর শ্রেণীর অস্তিত্ব নেই, বা আপনি কোনও এমন পদ্ধতির জন্য আহ্বান করছেন যা একটি অন্তর্নিহিত প্যারামিটারের প্রয়োজন হয়, এটি এমন একটি অন্তর্নিহিত অনুসন্ধান করবে যা প্রয়োজনীয়তার সাথে উপযুক্ত হবে ।

এই অনুসন্ধানটি নির্দিষ্ট বিধিগুলি মেনে চলে যা সংজ্ঞায়িত করে যে কোন ফলগুলি দৃশ্যমান এবং কোনটি নয়। নিম্নলিখিত সারণীটি বোঝাচ্ছে যেখানে সংকলক জড়িতদের জন্য অনুসন্ধান করবে তা জোশ সুরেথের দ্বারা প্রভাবিত হওয়া সম্পর্কে একটি দুর্দান্ত উপস্থাপনা থেকে নেওয়া হয়েছিল , যা আমি তাদের স্কাল জ্ঞানের উন্নতি করতে চাইলে আন্তরিকভাবে সুপারিশ করি। প্রতিক্রিয়া এবং আপডেটের পরে এটি পরিপূরক হয়েছে।

নীচে ১ নম্বর নীচে থাকা ইমপ্লিটগুলি 2 নং এর অধীনগুলির চেয়ে বেশি প্রাধান্য পেয়েছে that এটির বাইরেও যদি বেশ কয়েকটি যোগ্য যুক্তি থাকে যা অন্তর্নিহিত প্যারামিটারের ধরণের সাথে মেলে, স্থিতিশীল ওভারলোডিং রেজোলিউশনের নিয়ম ব্যবহার করে একটি সুনির্দিষ্ট নির্দিষ্টটি বেছে নেওয়া হবে (স্কেলা দেখুন) নির্দিষ্টকরণ .6.26.3) .3 এই উত্তরটির শেষে আমি সংযুক্ত একটি প্রশ্নের মধ্যে আরও বিস্তারিত তথ্য পাওয়া যাবে।

  1. বর্তমানের সুযোগে প্রথম চেহারা
    • প্রভাব বর্তমানের স্কোপে সংজ্ঞায়িত
    • সুস্পষ্ট আমদানি
    • ওয়াইল্ডকার্ড আমদানি
    • অন্যান্য ফাইলগুলিতে একই সুযোগ
  2. এখন সম্পর্কিত প্রকারগুলি দেখুন
    • এক প্রকারের সহযোগী বস্তু
    • একটি আর্গুমেন্টের ধরণের অন্তর্নিহিত সুযোগ (২.৯.১)
    • ধরণের আর্গুমেন্টের অন্তর্নিহিত সুযোগ (২.৮.০)
    • নেস্টেড প্রকারের জন্য বাহ্যিক বস্তু
    • অন্যান্য মাত্রা

আসুন তাদের জন্য কয়েকটি উদাহরণ দিন:

ফলস্বরূপ ব্যাপ্তিতে সংজ্ঞা দেওয়া

implicit val n: Int = 5
def add(x: Int)(implicit y: Int) = x + y
add(5) // takes n from the current scope

সুস্পষ্ট আমদানি

import scala.collection.JavaConversions.mapAsScalaMap
def env = System.getenv() // Java map
val term = env("TERM")    // implicit conversion from Java Map to Scala Map

ওয়াইল্ডকার্ড আমদানি

def sum[T : Integral](list: List[T]): T = {
    val integral = implicitly[Integral[T]]
    import integral._   // get the implicits in question into scope
    list.foldLeft(integral.zero)(_ + _)
}

অন্যান্য ফাইলগুলিতে একই সুযোগ

সম্পাদনা : দেখে মনে হচ্ছে এর আলাদা নজির নেই। আপনার যদি এমন কিছু উদাহরণ রয়েছে যা একটি নজিরের পার্থক্য দেখায়, দয়া করে একটি মন্তব্য করুন। অন্যথায়, এই এক উপর নির্ভর করবেন না।

এটি প্রথম উদাহরণের মতো, তবে অনুমানযোগ্য সংজ্ঞাটি এর ব্যবহারের চেয়ে আলাদা ফাইলে রয়েছে। কীভাবে প্যাকেজ অবজেক্টগুলি কার্যকর করতে আনতে ব্যবহৃত হতে পারে তা দেখুন ।

এক প্রকারের কম্পেনিয়ান অবজেক্টস

এখানে নোটের দুটি অবজেক্ট সহচর রয়েছে। প্রথমে "উত্স" প্রকারের অবজেক্টের সহচরটিকে সন্ধান করা হবে। উদাহরণস্বরূপ, অবজেক্টের অভ্যন্তরে Optionএকটি অন্তর্নিহিত রূপান্তর রয়েছে Iterable, সুতরাং কেউ Iterableপদ্ধতিগুলিতে কল করতে পারে Optionবা কোনওটি Optionপ্রত্যাশা করে পাস করতে পারে Iterable। উদাহরণ স্বরূপ:

for {
    x <- List(1, 2, 3)
    y <- Some('x')
} yield (x, y)

এই অভিব্যক্তিটি অনুবাদক দ্বারা অনুবাদ করেছেন

List(1, 2, 3).flatMap(x => Some('x').map(y => (x, y)))

যাইহোক, List.flatMapএকটি আশা TraversableOnce, যা Optionহয় না। সংকলকটি তখন Optionএর অবজেক্টের সহচরের অভ্যন্তরের দিকে তাকায় এবং রূপান্তরটি সন্ধান করে Iterableযা একটি TraversableOnce, যা এই অভিব্যক্তিটিকে সঠিক করে তোলে।

দ্বিতীয়ত, প্রত্যাশিত ধরণের সহযোগী অবজেক্ট:

List(1, 2, 3).sorted

পদ্ধতিটি sortedনিহিত থাকে Ordering। এই ক্ষেত্রে, এটি বস্তুর ভিতরে দেখায় Ordering, শ্রেণীর সহকর্মী Orderingএবং Ordering[Int]সেখানে একটি অন্তর্নিহিত খুঁজে পায় ।

নোট করুন যে সুপার ক্লাসগুলির সহযোগী অবজেক্টগুলিও অনুসন্ধান করা হয়। উদাহরণ স্বরূপ:

class A(val n: Int)
object A { 
    implicit def str(a: A) = "A: %d" format a.n
}
class B(val x: Int, y: Int) extends A(y)
val b = new B(5, 2)
val s: String = b  // s == "A: 2"

এভাবেই স্ক্যালাল অন্তর্নিহিত Numeric[Int]এবং Numeric[Long]আপনার প্রশ্নের মধ্যে খুঁজে পেয়েছিল, যেমন তারা ভিতরে খুঁজে পাওয়া যায় Numeric, না Integral

একটি আর্গুমেন্টের প্রকারের অন্তর্নিহিত ব্যাপ্তি

আপনার যদি একটি আর্গুমেন্ট টাইপ সহ কোনও পদ্ধতি থাকে Aতবে প্রকারের অন্তর্নিহিত সুযোগটিও Aবিবেচনা করা হবে। "অন্তর্নিহিত সুযোগ" দ্বারা আমি বোঝাতে চাইছি যে এই সমস্ত বিধিগুলি পুনরাবৃত্তির সাথে প্রয়োগ করা হবে - উদাহরণস্বরূপ, Aউপরের নিয়ম অনুসারে এর সহকর্মী অবজেক্টগুলি নিদর্শনগুলির জন্য অনুসন্ধান করা হবে।

নোট করুন যে এর অর্থ এই নয় যে এই Aপরামিতিটির রূপান্তরগুলির জন্য অন্তর্নিহিত সুযোগটি অনুসন্ধান করা হবে, তবে পুরো অভিব্যক্তিটি। উদাহরণ স্বরূপ:

class A(val n: Int) {
  def +(other: A) = new A(n + other.n)
}
object A {
  implicit def fromInt(n: Int) = new A(n)
}

// This becomes possible:
1 + new A(1)
// because it is converted into this:
A.fromInt(1) + new A(1)

এটি স্কেলা ২.৯.১ থেকে উপলব্ধ।

প্রকার আর্গুমেন্টের অন্তর্নিহিত সুযোগ

প্রকারের শ্রেণীর প্যাটার্নটি সত্যই কার্যকর করতে এটি প্রয়োজন। Orderingউদাহরণস্বরূপ বিবেচনা করুন : এটি এর সহযোগী অবজেক্টে কিছু জড়িত নিয়ে আসে তবে আপনি এতে স্টাফ যোগ করতে পারবেন না। তাহলে আপনি কীভাবে Orderingনিজের ক্লাসের জন্য একটি স্বয়ংক্রিয়ভাবে সন্ধান করতে পারেন?

এর বাস্তবায়ন দিয়ে শুরু করা যাক:

class A(val n: Int)
object A {
    implicit val ord = new Ordering[A] {
        def compare(x: A, y: A) = implicitly[Ordering[Int]].compare(x.n, y.n)
    }
}

সুতরাং, আপনি কল করলে কী হয় তা বিবেচনা করুন

List(new A(5), new A(2)).sorted

যেমনটি আমরা দেখেছি, পদ্ধতিটি sortedএকটি প্রত্যাশা করে Ordering[A](আসলে, এটি একটি Ordering[B], কোথায় প্রত্যাশা করে B >: A)। ভিতরে এরকম কোনও জিনিস নেই Ordering, এবং কোনও "উত্স" টাইপ নেই যা দেখতে হবে। একথাও ঠিক যে, এটা ভিতরে খুঁজে পেতে হয় A, যা একটি হল টাইপ যুক্তি এর Ordering

বিভিন্ন সংগ্রহের পদ্ধতিগুলিও কীভাবে প্রত্যাশার CanBuildFromকাজটি করে: এর প্রকারের পরামিতিগুলির ক্ষেত্রে সহকর্মী অবজেক্টগুলির মধ্যে ফলস্বরূপগুলি পাওয়া যায় CanBuildFrom

দ্রষ্টব্য : Orderingসংজ্ঞায়িত করা হয় trait Ordering[T], যেখানে Tএকটি টাইপ পরামিতি। পূর্বে, আমি বলেছিলাম যে স্কালা প্রকারের প্যারামিটারগুলির ভিতরে দেখেছিল, যা বেশি বোঝায় না। অন্তর্নিহিত উপরে জন্য লাগছিল Ordering[A], যেখানে A, একটি প্রকৃত ধরনের পরামিতি টাইপ না: এটা হল টাইপ যুক্তি করতে Ordering। স্কালার স্পেসিফিকেশনের 7.2 বিভাগ দেখুন।

এটি স্কেলা ২.৮.০ থেকে উপলব্ধ।

নেস্টেড টাইপের জন্য আউটার অবজেক্টস

আমি আসলে এর উদাহরণ দেখিনি। কেউ যদি ভাগ করে নিতে পারে তবে আমি কৃতজ্ঞ থাকব। নীতিটি সহজ:

class A(val n: Int) {
  class B(val m: Int) { require(m < n) }
}
object A {
  implicit def bToString(b: A#B) = "B: %d" format b.m
}
val a = new A(5)
val b = new a.B(3)
val s: String = b  // s == "B: 3"

অন্যান্য মাত্রা

আমি নিশ্চিত যে এটি একটি রসিকতা ছিল, তবে এই উত্তরটি আপ টু ডেট নাও হতে পারে। তাই এই প্রশ্নটি যা হচ্ছে তার চূড়ান্ত সালিশী হিসাবে গ্রহণ করবেন না এবং যদি আপনি লক্ষ্য করেন যে এটি পুরানো হয়ে গেছে, দয়া করে আমাকে জানান যাতে আমি এটি ঠিক করতে পারি।

সম্পাদনা

আগ্রহের সম্পর্কিত প্রশ্ন:


60
আপনার উত্তরগুলি কোনও বইয়ে ব্যবহার করা শুরু করার এখন সময় এসেছে, এতক্ষণে এটি সমস্ত একসাথে রাখার বিষয়।
পেডরফুরলা

3
@pedrofurla আমাকে পর্তুগিজ ভাষায় একটি বই লেখার জন্য বিবেচনা করা হয়েছে। যদি কেউ আমাকে কোনও প্রযুক্তিগত প্রকাশকের সাথে যোগাযোগ করতে পারেন ...
ড্যানিয়েল সি সোব্রাল

2
প্রকারের অংশগুলির সহযোগীদের প্যাকেজ সামগ্রীগুলিও অনুসন্ধান করা হয়। lampsvn.epfl.ch/trac/scala/ticket/4427
retronym

1
এই ক্ষেত্রে, এটি অন্তর্ভুক্ত সুযোগের অংশ। কল সাইটটি সেই প্যাকেজের মধ্যে থাকা উচিত নয়। এটা আমার জন্য অবাক।
retronym

2
হ্যাঁ, তাই স্ট্যাকওভারফ্লো / প্রশ্নগুলি / 6262২২০৫৫ এটিকে বিশেষভাবে অন্তর্ভুক্ত করে তবে আমি লক্ষ্য করেছি যে আপনি লিখেছেন "নিম্নলিখিত তালিকাটি অগ্রাধিকার ক্রমে উপস্থাপিত করার উদ্দেশ্যে ... ... দয়া করে রিপোর্ট করুন।" মূলত, অভ্যন্তরীণ তালিকাগুলি আনঅর্ডার করা উচিত যেহেতু এগুলির সকলের সমান ওজন রয়েছে (কমপক্ষে 2.10 এ)।
ইউজিন ইয়োকোটা

23

আমি অন্তর্নিহিত প্যারামিটার রেজোলিউশনের নজিরটি সন্ধান করতে চেয়েছিলাম, যেখানে এটি সন্ধান করা হয় তাই নয়, তাই আমি একটি ব্লগ পোস্ট লিখেছিলাম আমদানি শুল্ক ছাড়াই ইমপ্লিটগুলি পর্যালোচনা করে (এবং কিছু প্রতিক্রিয়ার পরে আবারও প্যারামিটারের অগ্রাধিকার )।

তালিকাটি এখানে:

  • 1) স্থানীয় ঘোষণাপত্র, আমদানি, বাহ্যিক সুযোগ, উত্তরাধিকার, প্যাকেজ অবজেক্টের মাধ্যমে প্রিফিক্স ছাড়াই অ্যাক্সেসযোগ্য প্যাকেজ অবজেক্টের মাধ্যমে বর্তমান অনুরোধের স্কোপটিতে দৃশ্যমান প্রভাবিত।
  • ২) অন্তর্নিহিত স্কোপ , যার মধ্যে সমস্ত ধরণের সাহাবী অবজেক্ট এবং প্যাকেজ অবজেক্ট থাকে যা অন্তর্ভুক্ত প্রকারের সাথে কিছুটা সম্পর্ক রাখে যা আমরা অনুসন্ধান করি (অর্থাত্ টাইপের প্যাকেজ অবজেক্ট, প্রকারের সহযোগী অবজেক্টটি, তার ধরণের কনস্ট্রাক্টরের যদি থাকে তবে) এর পরামিতিগুলি যদি থাকে তবে এবং এর সুপারটাইপ এবং সুপাররেটগুলিও)।

যদি উভয় পর্যায়ে আমরা একাধিক নিখুঁত খুঁজে পাই তবে এটি সমাধানের জন্য স্ট্যাটিক ওভারলোডিং নিয়ম ব্যবহৃত হয়।


3
আপনি প্যাকেজ, অবজেক্টস, বৈশিষ্ট্য এবং ক্লাসগুলি সংজ্ঞায়িত করে এবং স্কোপটি উল্লেখ করার সময় তাদের চিঠিগুলি ব্যবহার করে কিছু কোড লিখলে এটি উন্নত হতে পারে। কোনও নামকরণের ঘোষণা দেওয়ার দরকার নেই - কেবল নাম এবং কে প্রসারিত করে, এবং কোন সুযোগে।
ড্যানিয়েল সি সোব্রাল
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.