trait Base {
private def _name() = "Base"
def func = _name
}
trait A extends Base {
private def _name() = "A"
override def func = _name + " extends " + super.func
}
trait B extends Base {
private def _name() = "B"
override def func = _name + " extends " + super.func
}
val x = new Base with A with B
val y = new Base with B with A
println(x.func)
println(y.func)
Hogy mi készül éppen? Demonstrációs célokból szükségem van egy egyszerű Network-on-a-Chip szimulátorra, és úgy gondolom, az Actor model ennek leírására kiválóan használható (esetünkben Akka).
Scala rulez, teljesen rajongója lettem.
- wachag blogja
- A hozzászóláshoz be kell jelentkezni
- 1003 megtekintés
Hozzászólások
Örülök, hogy neked is tetszik!
Annyi kiegészítést tennék a kódhoz, hogy nem szokás aláhúzással kezdeni az azonosítókat, illetve ami nem függvény, hanem konstans, azt lehet val-lal definiálni akkor is, ha a szülőben def-volt, mint itt a name, illetve nem szükséges a végére a zárójel, ahogy a func végére se tettél. (A func végére inkább lehetne rakni, mert az tényleg függvény, szemben a name-mel).
Én inkább ilyenre írnám:
trait Base {
private def name = "Base"
def func = name
}
trait A extends Base {
private val name = "A"
override def func = name + " extends " + super.func
}
trait B extends Base {
private val name = "B"
override def func = name + " extends " + super.func
}
val x = new Base with A with B
val y = new Base with B with A
println(x.func)
println(y.func)
- A hozzászóláshoz be kell jelentkezni
Ja, ez egy stack overflowos mintakód volt copypastelve, forrásmegjelölés nélkül, de köszi!
- A hozzászóláshoz be kell jelentkezni
Ha értékként akarod használni, akkor is, ha függvény, akkor szerintem a zárójelpár kerülendő. Azt érdemes megtartani azokra az esetekre, ha valamiért a függvénynek mellékhatása van.
- A hozzászóláshoz be kell jelentkezni
Egyébként ezekben a témakörökben (Scala, Akka, FP) mindig szívesen segítek, ha bármi kérdésed lenne!
- A hozzászóláshoz be kell jelentkezni
Köszi, egyelőre elvagyok vele, meg az idevágó Courserát végigvittem anno.
- A hozzászóláshoz be kell jelentkezni
Ld. decorator pattern.
- A hozzászóláshoz be kell jelentkezni
Nem teljesen, vagy legalábbis itt szimplán származtatással csinálod ezt meg. Nem kell adattag, nem kell külön forwardolás.
- A hozzászóláshoz be kell jelentkezni
Csak arra gondoltam, hogy frappánsabb lett volna úgy megfogalmazni a posztot, hogy "Decorator pattern Scala-ban". Mert ez az, némi nyelvi támogatással.
- A hozzászóláshoz be kell jelentkezni
Ha kérdeztem volna a fórumban, akkor ezt kérdeztem volna, hogy hogyan lehet ezt szépen megvalósítani :-).
- A hozzászóláshoz be kell jelentkezni
Lehet Scalaban "rendes" decorator pattern megvalósítást is írni. Illetve extrém esetben megvalósítható teljesen funkcionálisan is. :)
- A hozzászóláshoz be kell jelentkezni
> Illetve extrém esetben megvalósítható teljesen funkcionálisan is. :)
+1
Szerintem nem csak extrém esetben! :) Itt egy jó anyag hozzá.
- A hozzászóláshoz be kell jelentkezni
Extrém alatt azt értettem, hogy tekinthetünk úgy is egy függvényre, mint egy olyan objektumra, aminek egyetlen metódusa van, vagyis a decorator design pattern egy metódusos megvalósítása gyakorlatilag ekvivalens a funkcionális megvalósítással: http://ideone.com/gU26Jg
- A hozzászóláshoz be kell jelentkezni
Így már értem, hogy pontosan mire gondoltál. Azt hittem az extrémet szélsőséges értelemben érted, vagyis, hogy meg lehet csinálni úgy is, de nem igazán jó megoldás. Holott a példádból is látszik, hogy egyszerűbb és sokkal általánosabban, rugalmasabban használható az FP megoldás, mint az OO.
- A hozzászóláshoz be kell jelentkezni
Amire folyamatosan rádöbbenek: eszméletlenül kifejező nyelv, nagyon jóleg tudom vele fogalmazni a dolgokat.
Egy valami hiányzik most: generikus osztályban az adott típusból példány létrehozása. No jó, meg a parametrizálható trait-ek, de azt tagváltozókkal meg lehet oldani.
- A hozzászóláshoz be kell jelentkezni
"generikus osztályban az adott típusból példány létrehozása."
Ez alatt mit értesz? Esetleg egy példa is jó volna, hogy mire használnád!
Szerk.: Közben rájöttem, hogy valószínűleg a generikus típusból szeretnél létrehozni egy példányt az osztályon belül. Erre az a megoldás, hogy megadsz az osztályodnak egy factory függvényt, ami létrehozza, lásd itt.
- A hozzászóláshoz be kell jelentkezni
Vagy reflectionnel: http://ideone.com/lqSCQw ;)
- A hozzászóláshoz be kell jelentkezni
"Malomkövekért kiáltasz, mikor csak a ceruzádat akarod meghegyezni" :-)
- A hozzászóláshoz be kell jelentkezni
ez a manifest valami olyasmi, hogy compile time addig alakítgatja a metódusom, amíg runtime nem lesz ott egy Class (Javasan?)
(pongyolán fogalmazva - nagyon nem értek a Scalahoz)
--
blogom
- A hozzászóláshoz be kell jelentkezni
A fordítási időben még ismert típushoz tartozó Manifest-et a fordító képes fordítási időben implicit elérhetővé tenni, így futás időben olyan információt ad a típusról, ami egyébként a type erasure miatt elveszne. Ennek csak egy aspektusa az, hogy elérhetővé válik a típushoz tartozó Class objektum.
Ahogy írtam, a Manifest-et implicit adja oda a fordító, a lenti két metódus (majdnem) ekvivalens:
def foo[T : Manifest] = manifest[T].toString
def bar[T](implicit mf: Manifest[T]) = mf.toString
A fenti kódban a manifest[T] visszaadja az implicit elérhető Manifest[T]-t, így lehetne implementálni (ez egy gyakori trükk Scalaban implicitek környékén, pl. az Ordering is tud ilyesmit):
def manifest[T](implicit mf: Manifest[T]) = mf
A Manifestben az a jó, hogy a fordító mindjárt tudja is használni, pl: http://ideone.com/VUcCfB
- A hozzászóláshoz be kell jelentkezni
ez menő. viszonylag sokat gondolkoztam mostanság azon, miért nem csináltak valami hasonlót Java-ban - ha már a generikusok csak így kókányolva kerültek be a nyelvbe. s hogy van-e, lenne-e bármilyen akadálya, akár nyelvi szinten, akár JVM szinten. De ezek szerint JVM szinten nincs.
az ideone-os példában azt kéne látnom, hogy a fordító a generikus típus alapján tudja, hogy a két függvényhívás közül (
safePrint2[Int](iToS) orElse safePrint2[Boolean](bToS)
) melyiket is kell majd meghívnia?
Ez bájtkódban hogy jelenik meg? A safePrint2 függvényből kettő lesz bátjkód szinten?
Mindenesetre ez bookmark, köszi szépen - ha lesz egy kis időm, a vizsgaidőszak után, alaposabban belemélyedek ebbe, elég érdekesnek tűnik.
--
blogom
- A hozzászóláshoz be kell jelentkezni
"a fordító a generikus típus alapján tudja, hogy a két függvényhívás közül (
safePrint2[Int](iToS) orElse safePrint2[Boolean](bToS)
) melyiket is kell majd meghívnia?
Ez bájtkódban hogy jelenik meg? A safePrint2 függvényből kettő lesz bátjkód szinten?"
A safePrint2 egy PartialFunctiont (továbbiakban PF) ad vissza, ami egy olyan függvény, ami csak a paraméterei értelmezési tartományának egy részén értelmezett, ezt írja le a case utáni kifejezés. Jelen esetben azt mondtam meg, hogy az x típusa legyen T, miközben maga a PF Any-t (vagyis java Objectet) vár. Az implicit Manifest[T]-ből JVM szinten egy explicit paraméter lesz, ami majd a hívás helyén kerül behelyettesítésre a fordító által. Ezt a Manifestet használja a PF implementációja belül, így tudja megkülönböztetni a paramétereket típus alapján futásidőben. Bytecodeban most nem tudod megmutatni, de decompileolva így néz ki (az evidence$1 használata az érdekes):
public <T> PartialFunction<Object, BoxedUnit> safePrint2(Function1<T, String> toString, final Manifest<T> evidence$1) {
new AbstractPartialFunction(toString, evidence$1) {
public static final long serialVersionUID = 0L;
public final boolean isDefinedAt(Object x2) {
Object localObject = x2;
Option localOption = this.evidence$1$1.unapply(localObject);
boolean bool;
if ((!localOption.isEmpty()) && ((localOption.get() instanceof Object))) {
bool = true;
} else {
bool = false;
}
return bool;
}
public final <A1, B1> B1 applyOrElse(A1 x2, Function1<A1, B1> default) {
final Object localObject1 = x2;
Option localOption = this.evidence$1$1.unapply(localObject1);
Object localObject2;
if ((!localOption.isEmpty()) && ((localOption.get() instanceof Object))) {
Predef..MODULE$.println(Try..MODULE$.apply(new AbstractFunction0() {
public static final long serialVersionUID = 0L;
public final String apply() {
return (String)X..anonfun.safePrint2.1.this.toString$2.apply(localObject1);
}
}));
localObject2 = BoxedUnit.UNIT;
}
else {
localObject2 = default.apply(x2);
}
return (B1)localObject2;
}
};
}
Így kerül meghívásra:
safePrint2(new AbstractFunction1() {
public static final long serialVersionUID = 0L;
public final String apply(int x) {
return X..this.iToS(x);
}
}, ManifestFactory..MODULE$.Int())
Az orElse egy sima metódus a PF-ön, ami létrehoz egy új kompozit PF-t a this-ből és a paraméterből. Ebben nincs semmi compiler magic, egyszerűen megpróbálja végrehajtani az első PF-t, ha az nem sikerül, fallbackel a másodikra.
- A hozzászóláshoz be kell jelentkezni