Mariusz Prowaźnik

o programowaniu w Javie, Scali i Clojure.


Niejawne konwersje i parametry w Scali

Ostatnio programuję trochę w Scali, więc trzeba by wziąć na tapetę coś z tego języka. Pokażę na przykładach użycie niejawnych konwersji i parametrów.

Niejawne parametry

package conversions

import scala.concurrent._
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global

object ImplicitsDemo extends App {

  val f = Future("hello")
  println(Await.result(f, 1 second))
}

Są prostsze sposoby w Scali na wypisanie "hello" niż używanie do tego Future, ale gdyby w tego Future opakowane było jakieś bardziej wzniosłe zadanie, np. ściągnięcie tweetów z tagiem scala, to byłby to z życia wzięty przykład użycia niejawnych parametrów. Gdzie tu jest niejawny parametr? Oto sygnatura metody Future.apply:

def apply[T](body: ⇒ T)(implicit executor: ExecutionContext): Future[T]

Dzięki temu, że executor jest parametrem niejawnym, nie trzeba go podawać w Future("hello"). Nie bierze się też znikąd, kod by się nie skompilował bez:

import scala.concurrent.ExecutionContext.Implicits.global

a tam się znajduje:

implicit lazy val global: ExecutionContextExecutor = impl.ExecutionContextImpl.fromExecutor(null: Executor)

Kompilator widząc taki niejawny parametr, szuka zdefiniowanej wartości o odpowiednim typie z modyfikatorem implicit. Jest kilka rzeczy, na które trzeba uważać:

  • implicit dotyczy całej listy parametrów, zatem jeśli funkcja jest zdefiniowana:

     def f(a: String)(implicit b: Int, c: Int) = ???

    to b, c są niejawne.

  • kompilator wyszukuje wartość do wstawienia po typie, więc lepiej używać jakichś typów w miarę specyficznych dla naszego kodu - np zamiast użyć Int, albo String, lepiej opakować to we własny typ.

  • wstawiany "implicit" musi być dostępny jako pojedynczy identyfikator. Czyli, gdyby w przykładzie z Future trzeba zaimportować scala.concurrent.ExecutionContext.Implicits.global, a nie scala.concurrent.ExecutionContext.Implicits, bo kompilator nie wstawi Implicits.global.

Niejawne konwersje

Poniżej test z użyciem spec2 wzięty z szablonu "scala-test" z typesafe activators.

package specs2

import org.junit.runner.RunWith
import org.specs2.mutable.Specification
import org.specs2.runner.JUnitRunner

@RunWith(classOf[JUnitRunner])
class ListSpec extends Specification {
  
  "Calling isEmpty" should {
    "return true for a List with 0 elements" in {
      List().isEmpty must beTrue
    }
    "return false for List with > 0 elements" in {
      List(1, 2, 3).isEmpty must beFalse
    }
  }
}

String nie ma metody should, ale to działa dzięki niejawnym konwersjom, zdefiniowanym w kodzie spec2:

 implicit def described(s: String): Described = new Described(s)
 
  class Described(s: String) {
    def should(fs: =>Fragment) = addFragments(s, fs, "should")

 // reszta pominięta
  }

Kompilator widząc, że String nie ma odpowiedniej metody, szuka implicita, który może skonwertować String w typ, który taką metodę ma (tu: Described). Podobnie jest, gdy funkcja spodziewa się parametru o określonym typie, ale dostaje inny - jeśli jest zdefiniowana odpowiednia niejawna konwersja, to kompilator ją zastosuje.

Źródła:


O spotkaniach wzorowanych na Daily Scrum

Przez niecały rok miałem przyjemność pracować w zwinnym zespole, który z powodzeniem wprowadził Scrum - w całości. Byłem zaskoczony jak bardzo można poprawić sposób pracy nad projektem IT. Zmieniło się też moje podejście do jednej z praktyk Scrumowych: Codzienny Scrum (ang. The Daily Scrum).

Jest to praktyka wyglądająca na najprostszą do wprowadzenia ze wszystkich praktyk Scrum, dlatego często idzie na pierwszy ogień podczas prób uczynienia pracy zespołu zwinniejszą. No cóż, poprawne przeprowadzenie takiego spotkania rzeczywiście nie jest trudne, ale pod warunkiem, że jest osadzone w ramach Scruma. Gdyby jednak próbować robić podobne spotkania w odizolowaniu od reszty praktyk, to wręcz nie da się tego zrobić "zgodnie ze sztuką". Oczywiście, nawet krótkie codzienne spotkania zespołu bez przejmowania się jakimikolwiek zasadami Scruma mogą usprawniać pracę, ale to tylko ułamek potencjalnych korzyści.

Zajrzyjmy do Scrum Guide:

Codzienny Scrum jest spotkaniem dla Zespołu Deweloperskiego, ograniczonym czasowo do piętnastu minut, podczas którego bieżące zadania są synchronizowane i powstaje plan działania na najbliższe 24 godziny. Jest to osiągane poprzez inspekcję prac, które zostały wykonane od ostatniego Codziennego Scruma i prognozowaniu prac, które mogą zostać wykonane przed kolejnym spotkaniem.

Scrum Guide

Codzienny Scrum jest dla Zespołu Deweloperskiego. Nie jest po to, żeby zdać raport menadżerowi, czy jakiejś innej nadzorującej projekt osobie. Wyrazistym antywzorcem jest sytuacja, gdy menadżer przepytuje po kolei każdą osobę w zespole, a ci którzy już są przepytani, lub czekają na swoją kolej, zajmują się innymi rzeczami, bo to o czym mowa nie jest dla nich istotne, albo dlatego, że spotkanie trwa już 40 minut. Codzienny Scrum to przede wszystkim wymiana informacji pomiędzy członkami Zespołu Deweloperskiego i powinna być na tyle konkretna, by każdy każdego słuchał. Trudno uniknąć problemów na tym polu, gdy rzeczywiste role w zespole nie odpowiadają rolom ze Scruma.

Podczas Codziennego Scruma bieżące zadania są synchronizowane i powstaje plan na następne 24 godziny. Zespół decyduje które zadania najlepiej zrobić w ciągu następnego dnia oraz kto konkretnie je zrobi. Gorzej, jeśli nie ma w tej kwesti wiele do gadania, bo zdecydował o tym ktoś inny i jeśli zrobią inaczej to "będzie dym"... Kolejny możliwy problem polega na tym, że zespół nie może zaplanować dobrze pracy, bo potrzebuje do tego pomocy z zewnątrz zespołu.

Pierwsze z pytań Codziennego Scruma brzmi "co zostało zrobione", a nie "co było robione". Nie zrealizuje się jednak takiego podejścia w praktyce, jeśli praca nie jest rozbita na zadania, które da się zrobić w jeden dzień. Do tego trzeba wcześniejszego Planowania Sprintu (ang. Sprint Planning) oraz jeśli nie poświęca się regularnie czasu na Pielęgnowanie Rejestru Produktu (ang. Backlog Refinement). Nawet wtedy rozbicie pracy na takie zadania może być niemożliwe, jeśli dług techniczny jest zbyt duży.

Ponadto, bez Retrospektyw można nawet nie zauważyć, że coś jest nie tak.

Zespół, który chce być zwinny, prędzej czy później natrafia na jakąś Scrumową zasadę, którą w jego przypadku ciężko wdrożyć. Może wtedy ją olać. Może próbować ją wdrożyć "na pałę" i dojść po krótkim czasie do wniosku, że to bez sensu. Prawdziwa poprawa następuje dopiero wtedy, gdy rozpoznaje się kryjący za tym wszystkim problem i podejmuje odpowiednie środki naprawcze.

PS. W zgodzie z najnowszymi trendami na blogach IT dorzucam jakieś zdjęcie: kaczki i gołąb z Parku Łazienkowskiego.