Davul Sıralayıcıdan Karaağaç Öğrenmek (1. Kısım)

Yayınlanan: 2022-03-10
Kısa özet ↬ Ön uç geliştirici Brian Holt, okuyuculara Elm'de bir davul sıralayıcı oluşturma konusunda rehberlik ediyor. Bu iki bölümlük dizinin birinci bölümünde Elm'in sözdizimini, kurulumunu ve temel kavramlarını tanıtıyor. Basit uygulamalar oluşturmak için Elm mimarisiyle nasıl çalışacağınızı öğreneceksiniz.

Tek sayfalı uygulamaların (SPA) evrimini izleyen bir ön uç geliştiriciyseniz, Redux'a ilham veren işlevsel dil olan Elm'i muhtemelen duymuşsunuzdur. Henüz yapmadıysanız, React, Angular ve Vue gibi SPA projeleriyle karşılaştırılabilir bir JavaScript derleme dilidir.

Bunlar gibi, kodu daha sürdürülebilir ve performanslı hale getirmeyi amaçlayan sanal dom aracılığıyla durum değişikliklerini yönetir. Geliştirici mutluluğuna, yüksek kaliteli araçlara ve basit, tekrarlanabilir kalıplara odaklanır. Temel farklarından bazıları, statik olarak yazılmış olması, harika bir şekilde yardımcı hata mesajları ve işlevsel bir dil olmasıdır (Nesne Yönelimli'nin aksine).

Girişim, Elm'in yaratıcısı Evan Czaplicki'nin ön uç geliştirici deneyimi ve buna bağlı olarak Elm'in vizyonu hakkında yaptığı bir konuşmayla geldi. Birisi ön uç geliştirmenin sürdürülebilirliği ve kullanılabilirliğine de odaklandığı için konuşması beni gerçekten etkiledi. Elm'i bir yıl önce bir yan projede denedim ve programlamaya başladığımdan beri sahip olmadığım bir şekilde hem özelliklerinden hem de zorluklarından zevk almaya devam ettim; Her şeye yeniden başlayan biriyim. Ayrıca, Elm'in birçok uygulamasını diğer dillerde de uygulayabildiğimi görüyorum.

Bağımlılık Bilinci Geliştirme

Bağımlılıklar her yerdedir. Bunları azaltarak, sitenizin çok çeşitli senaryolarda en fazla sayıda insan tarafından kullanılabilir olma olasılığını artırabilirsiniz.İlgili bir makaleyi okuyun →

Bu iki bölümlük makalede, Elm'de davul vuruşlarını programlamak için bir adım sıralayıcı oluşturacak ve dilin en iyi özelliklerinden bazılarını sergileyeceğiz. Bugün, Elm'deki temel kavramları, yani başlama, türleri kullanma, görünüm oluşturma ve durumu güncelleme konularından bahsedeceğiz. Bu makalenin ikinci kısmı, daha sonra, büyük yeniden düzenleyicileri kolayca ele alma, tekrarlayan etkinlikler ayarlama ve JavaScript ile etkileşim kurma gibi daha gelişmiş konulara dalacaktır.

Nihai projeyle burada oynayın ve kodunu buradan kontrol edin.

adım sıralayıcı çalışırken
İşte tamamlanmış adım sıralayıcı çalışırken nasıl görüneceği.

Elm'e Başlarken

Bu makaleyi takip etmek için tarayıcı içi bir Elm geliştirici deneyimi olan Ellie'yi kullanmanızı öneririm. Ellie'yi çalıştırmak için herhangi bir şey yüklemeniz gerekmez ve içinde tam işlevli uygulamalar geliştirebilirsiniz. Elm'i bilgisayarınıza kurmayı tercih ediyorsanız, kurulum yapmanın en iyi yolu resmi başlangıç ​​kılavuzunu takip etmektir.

Atlamadan sonra daha fazlası! Aşağıdan okumaya devam edin ↓

Bu makale boyunca, sıralayıcıyı yerel olarak geliştirmeme rağmen, devam etmekte olan Ellie sürümlerine bağlantı vereceğim. Ve CSS tamamen Elm'de yazılabilirken, bu projeyi PostCSS'de yazdım. Bu, stillerin yüklenmesi için yerel geliştirme için Elm Reaktöründe biraz yapılandırma gerektirir. Kısa olması adına, bu makalede stillere değinmeyeceğim, ancak Ellie bağlantıları tüm küçültülmüş CSS stillerini içeriyor.

Karaağaç, aşağıdakileri içeren bağımsız bir ekosistemdir:

  • karaağaç yapmak
    Elm kodunuzu derlemek için. Webpack, diğer varlıkların yanı sıra Elm projelerini üretmek için hala popüler olsa da, gerekli değildir. Bu projede, Webpack'i hariç tutmayı ve kodu derlemek için elm make make'ye güvenmeyi seçtim.
  • Karaağaç Paketi
    Topluluk tarafından oluşturulan paketleri/modülleri kullanmak için NPM ile karşılaştırılabilir bir paket yöneticisi.
  • karaağaç reaktörü
    Otomatik olarak derlenen bir geliştirme sunucusu çalıştırmak için. Daha da dikkat çekici olanı, uygulamanızın durumlarını gözden geçirmeyi ve hataları yeniden oynatmayı kolaylaştıran Zaman Yolculuğu Hata Ayıklayıcısını içerir.
  • karaağaç repliği
    Terminalde basit Elm ifadelerini yazmak veya test etmek için.

Tüm Elm dosyaları modules olarak kabul edilir. Herhangi bir dosyanın başlangıç ​​satırları, FileName değişmez dosya adı olduğu ve functions , diğer modüller tarafından erişilebilir hale getirmek istediğiniz genel işlevler olduğu, module FileName exposing (functions) içerecektir. Modül tanımından hemen sonra harici modüllerden içe aktarılır. Geri kalan işlevler bunu takip eder.

 module Main exposing (main) import Html exposing (Html, text) main : Html msg main = text "Hello, World!"

Main.elm adlı bu modül, main işlevi olan tek bir işlevi gösterir ve Html modülünden/paketinden Html ve text içe aktarır. main işlev iki bölümden oluşur: tür açıklaması ve gerçek işlev. Tip açıklamaları, fonksiyon tanımları olarak düşünülebilir. Argüman türlerini ve dönüş türünü belirtirler. Bu durumda, bizimki, main işlevin hiçbir argüman almadığını ve Html msg döndürdüğünü belirtir. İşlevin kendisi, "Merhaba, Dünya" içeren bir metin düğümü oluşturur. Bir fonksiyona argüman iletmek için fonksiyondaki eşittir işaretinden önce boşlukla ayrılmış isimler ekleriz. Ayrıca, argüman türlerini, argümanların sırasına göre, ardından bir okla tür ek açıklamasına ekleriz.

 add2Numbers : Int -> Int -> Int add2Numbers first second = first + second

JavaScript'te bunun gibi bir işlev karşılaştırılabilir:

 function add2Numbers(first, second) { return first + second; }

Ve TypeScript gibi bir Typed dilinde şöyle görünür:

 function add2Numbers(first: number, second: number): number { return first + second; }

add2Numbers iki tamsayı alır ve bir tamsayı döndürür. Her işlevin bir değer döndürmesi gerektiğinden , açıklamadaki son değer her zaman dönüş değeridir. 5 like add2Numbers 2 3 elde etmek için 2 ve 3 ile add2Numbers diyoruz.

React bileşenlerini bağladığınız gibi, derlenmiş Elm kodunu DOM'a bağlamamız gerekiyor. Bağlamanın standart yolu, modülümüzde embed() öğesini çağırıp DOM öğesini ona iletmektir.

 <script> const container = document.getElementById('app'); const app = Elm.Main.embed(container); <script>

Uygulamamız gerçekten hiçbir şey yapmasa da, Elm kodumuzu derlemek ve metin oluşturmak için yeterli şeye sahibiz. Ellie'de kontrol edin ve argümanları 26. satırda add2Numbers olarak değiştirmeyi deneyin.

Elm uygulaması eklenen sayıları işler
Eklenen sayıları ekranda görüntüleyen çıplak kemikli Elm uygulamamız.

Türlerle Veri Modelleme

JavaScript veya Ruby gibi dinamik olarak yazılan bir dilden gelen türler gereksiz görünebilir. Bu diller, çalışma süresi boyunca iletilen değerden hangi tür işlevlerin alacağını belirler. Yazma işlevleri genellikle daha hızlı kabul edilir, ancak işlevlerinizin birbiriyle düzgün bir şekilde etkileşime girmesini sağlamanın güvenliğini kaybedersiniz.

Buna karşılık, Elm statik olarak yazılmıştır. İşlevlere iletilen değerlerin çalışma zamanından önce uyumlu olmasını sağlamak için derleyicisine güvenir. Bu, kullanıcılarınız için çalışma zamanı istisnası olmaması anlamına gelir ve Elm, "çalışma zamanı istisnası yok" garantisini bu şekilde sağlayabilir. Birçok derleyicideki tür hatalarının özellikle şifreli olabileceği durumlarda Elm, bunların anlaşılmasını ve düzeltilmesini kolaylaştırmaya odaklanır.

Karaağaç, türlerle başlamayı çok kolay hale getirir. Aslında, Elm'in tür çıkarımı o kadar iyidir ki, daha rahat olana kadar ek açıklama yazmayı atlayabilirsiniz. Türler konusunda yeniyseniz, bunları kendiniz yazmaya çalışmak yerine derleyicinin önerilerine güvenmenizi öneririm.

Verilerimizi türleri kullanarak modellemeye başlayalım. Adım sıralayıcımız, belirli bir davul örneğinin ne zaman çalınacağını gösteren görsel bir zaman çizelgesidir. Zaman çizelgesi, her biri belirli bir davul örneği ve adım sırası ile atanmış parçalardan oluşur. Bir adım , zaman içinde bir an veya bir vuruş olarak kabul edilebilir. Eğer bir adım aktifse , örnek çalma sırasında tetiklenmeli ve adım aktif değilse örnek sessiz kalmalıdır. Oynatma sırasında, sıralayıcı, aktif adımların örneklerini oynayarak her adımda hareket edecektir. Oynatma hızı Dakikadaki Vuruş Sayısı (BPM) tarafından belirlenir.

adım dizileri ile parçalardan oluşan son uygulama
Adım dizileriyle parçalardan oluşan son uygulamamızın ekran görüntüsü.

Uygulamamızı JavaScript'te Modelleme

Türlerimiz hakkında daha iyi bir fikir edinmek için, bu davul sıralayıcının JavaScript'te nasıl modelleneceğini düşünelim. Bir dizi parça var. Her iz nesnesi kendisi hakkında bilgi içerir: iz adı, tetiklenecek örnek/klip ve adım değerleri dizisi.

 tracks: [ { name: "Kick", clip: "kick.mp3", sequence: [On, Off, Off, Off, On, etc...] }, { name: "Snare", clip: "snare.mp3", sequence: [Off, Off, Off, Off, On, etc...] }, etc... ]

Oynatma ve durdurma arasındaki oynatma durumunu yönetmemiz gerekiyor.

 playback: "playing" || "stopped"

Oynatma sırasında hangi adımın oynanması gerektiğini belirlememiz gerekiyor. Ayrıca oynatma performansını da göz önünde bulundurmalıyız ve bir adım her artırıldığında her bir parçadaki her bir diziyi geçmek yerine; tüm aktif adımları tek bir oynatma dizisine indirgemeliyiz. Oynatma sırasındaki her koleksiyon, çalınması gereken tüm örnekleri temsil eder. Örneğin, ["kick", "hat"] , kick ve hi-hat örneklerinin oynaması gerektiği anlamına gelirken, ["hat"] yalnızca hi-hat'ın oynaması gerektiği anlamına gelir. Ayrıca her koleksiyona benzersizliği örnekle sınırlamak için ihtiyacımız var, böylece ["hat", "hat", "hat"] gibi bir şeyle sonuçlanmıyoruz.

 playbackPosition: 1 playbackSequence: [ ["kick", "hat"], [], ["hat"], [], ["snare", "hat"], [], ["hat"], [], ... ],

Ve oynatma hızını veya BPM'yi ayarlamamız gerekiyor.

 bpm: 120

Karaağaç Tipleriyle Modelleme

Bu verileri Elm türlerine dönüştürmek, esasen verilerimizin neyden yapılmasını beklediğimizi açıklamaktır. Örneğin, veri modelimize zaten model olarak atıfta bulunuyoruz, bu yüzden buna tür takma adıyla diyoruz. Tür takma adları, kodun daha kolay okunmasını sağlamak için kullanılır. Boolean veya tamsayı gibi ilkel bir tür değiller; onlar sadece ilkel bir tür veya veri yapısı verdiğimiz isimlerdir. Birini kullanarak, model yapımızı takip eden herhangi bir veriyi anonim bir yapı yerine bir model olarak tanımlarız. Birçok Elm projesinde ana yapıya Model adı verilir.

 type alias Model = { tracks : Array Track , playback : Playback , playbackPosition : PlaybackPosition , bpm : Int , playbackSequence : Array (Set Clip) }

Modelimiz biraz JavaScript nesnesine benzese de, bir Elm Record'u açıklıyor. Kayıtlar, ilgili verileri kendi tür açıklamaları olan çeşitli alanlarda düzenlemek için kullanılır. field.attribute kullanarak erişimleri kolaydır ve daha sonra göreceğimiz gibi güncellemeleri kolaydır. Nesneler ve kayıtlar, birkaç temel farklılık dışında birbirine çok benzer:

  • Var olmayan alanlar çağrılamaz
  • Alanlar asla null veya undefined olmayacak
  • this ve self kullanılamaz

Parça koleksiyonumuz üç olası türden birinden oluşabilir: Listeler, Diziler ve Kümeler. Kısacası, Listeler indekslenmemiş genel kullanım koleksiyonlarıdır, Diziler indekslenir ve Kümeler sadece benzersiz değerler içerir. Hangi iz adımının değiştirildiğini bilmek için bir indekse ihtiyacımız var ve diziler indekslendiğinden, bu bizim en iyi seçimimiz. Alternatif olarak, parçaya bir kimlik ekleyebilir ve bir Listeden filtreleyebiliriz.

Modelimizde, izleri bir dizi iz olarak dizdik, başka bir kayıt: tracks : Array Track . Track kendisi ile ilgili bilgileri içerir. Hem ad hem de klip dizelerdir, ancak diğer işlevler tarafından kodun başka bir yerinde başvurulacağını bildiğimiz için klibi takma adını yazdık. Takma ad vererek, kendi kendini belgeleyen kod oluşturmaya başlıyoruz. Türler ve tür takma adları oluşturmak, geliştiricilerin veri modelini iş modeline göre modellemesine ve her yerde bulunan bir dil oluşturmasına olanak tanır.

 type alias Track = { name : String , clip : Clip , sequence : Array Step } type Step = On | Off type alias Clip = String

Dizinin bir dizi açma/kapama değeri olacağını biliyoruz. Bunu, sequence : Array Bool gibi bir boole dizisi olarak ayarlayabiliriz, ancak iş modelimizi ifade etme fırsatını kaçırmış oluruz! Adım sıralayıcıların adımlardan oluştuğunu düşünürsek, Adım adında yeni bir tür tanımlıyoruz. Bir Step, bir boolean için bir tür takma adı olabilir, ancak bir adım daha ileri gidebiliriz: Adımların açık ve kapalı olmak üzere iki olası değeri vardır, bu nedenle birleşim türünü bu şekilde tanımlarız. Artık adımlar yalnızca Açık veya Kapalı olabilir, bu da diğer tüm durumları imkansız hale getirir.

playbackSequence için başka bir tür tanımlarız, Playback için bir takma addır ve PlaybackPosition Klip kümelerini içeren bir Dizi olarak tanımlarken Clip kullanırız. BPM, standart bir Int olarak atanır.

 type Playback = Playing | Stopped type alias PlaybackPosition = Int

Türlere başlarken biraz daha fazla ek yük olsa da, kodumuz çok daha sürdürülebilir. Kendi kendini belgeliyor ve iş modelimiz ile her yerde bulunan bir dil kullanıyor. Gelecekteki işlevlerimizin test gerektirmeden verilerimizle beklediğimiz şekilde etkileşime gireceğini bilme konusunda kazandığımız güven, bir açıklama yazmak için harcanan zamana değer. Ve türleri önermek için derleyicinin tür çıkarımına güvenebiliriz, böylece onları yazmak kopyalayıp yapıştırmak kadar basittir. İşte tam tip bildirimi.

Elm Mimarisini Kullanma

Elm Mimarisi, dilde doğal olarak ortaya çıkan basit bir durum yönetimi modelidir. İş modeli etrafında odak oluşturur ve son derece ölçeklenebilirdir. Diğer SPA çerçevelerinin aksine, Elm mimarisi hakkında fikir sahibidir - tüm uygulamaların yapılandırılma şekli budur, bu da onboarding'i bir esinti haline getirir. Mimari üç bölümden oluşur:

  • Uygulamanın durumunu ve takma ad yazdığımız yapıyı içeren model
  • Durumu güncelleyen güncelleme işlevi
  • Ve durumu görsel olarak oluşturan görüntüleme işlevi

Gittikçe pratikte Elm Mimarisini öğrenerek davul sıralayıcımızı oluşturmaya başlayalım. Uygulamamızı başlatarak, görünümü oluşturarak ve ardından uygulama durumunu güncelleyerek başlayacağız. Bir Ruby arka planından geldiğim için, daha kısa dosyaları tercih etme ve Elm işlevlerimi modüllere ayırma eğilimindeyim, ancak büyük Elm dosyalarına sahip olmak çok normal. Ellie'de bir başlangıç ​​noktası oluşturdum, ancak yerel olarak aşağıdaki dosyaları oluşturdum:

  • Types.elm, tüm tür tanımlarını içerir
  • Programı başlatan ve çalıştıran Main.elm
  • Durumu yöneten güncelleme işlevini içeren Update.elm
  • HTML'ye dönüştürülecek Elm kodunu içeren View.elm

Uygulamamızı Başlatma

Küçükten başlamak en iyisidir, bu nedenle modeli, açılıp kapanan adımları içeren tek bir iz oluşturmaya odaklanacak şekilde azaltıyoruz. Tüm veri yapısını zaten bildiğimizi düşünürken , küçükten başlamak, izleri HTML olarak oluşturmaya odaklanmamızı sağlar. Karmaşıklığı azaltır ve buna ihtiyacınız olmayacak kodu. Daha sonra, derleyici modelimizi yeniden düzenleme konusunda bize rehberlik edecektir. Types.elm dosyasında Step ve Clip türlerimizi koruyoruz ancak modeli ve izi değiştiriyoruz.

 type alias Model = { track : Track } type alias Track = { name : String , sequence : Array Step } type Step = On | Off type alias Clip = String

Elm'i HTML olarak oluşturmak için Elm Html paketini kullanıyoruz. Birbiri üzerine inşa edilen üç tür program oluşturma seçeneklerine sahiptir:

  • Başlangıç ​​Programı
    Yan etkileri hariç tutan ve özellikle Elm Mimarisini öğrenmek için kullanışlı olan azaltılmış bir program.
  • programı
    Elm dışında var olan veritabanları veya araçlarla çalışmak için kullanışlı, yan etkileri işleyen standart program.
  • Bayraklı Program
    Varsayılan veriler yerine gerçek verilerle kendini başlatabilen genişletilmiş bir program.

Daha sonra derleyici ile değiştirmek kolay olduğundan, mümkün olan en basit program türünü kullanmak iyi bir uygulamadır. Elm'de programlama yaparken bu yaygın bir uygulamadır; sadece ihtiyacınız olanı kullanın ve daha sonra değiştirin. Amaçlarımız için, bir yan etki olarak kabul edilen JavaScript ile uğraşmamız gerektiğini biliyoruz, bu yüzden bir Html.program oluşturuyoruz. Main.elm'de, fonksiyonları alanlarına geçirerek programı başlatmamız gerekiyor.

 main : Program Never Model Msg main = Html.program { init = init , view = view , update = update , subscriptions = always Sub.none }

Programdaki her alan, uygulamamızı kontrol eden Elm Runtime'a bir fonksiyon iletir. Özetle, Elm Runtime:

  • Programı init başlangıç ​​değerlerimizle başlatır.
  • Başlatılan modelimizi view oluşturur.
  • Görünümlerden, komutlardan veya aboneliklerden update için iletiler iletildiğinde görünümü sürekli olarak yeniden işler.

Yerel olarak, view ve update işlevlerimiz sırasıyla View.elm ve Update.elm içe aktarılacak ve bunları birazdan oluşturacağız. subscriptions , güncellemelere neden olacak mesajları dinler, ancak şimdilik, always Sub.none atayarak bunları yok sayıyoruz. İlk fonksiyonumuz init modeli başlatır. init ilk yük için varsayılan değerler gibi düşünün. Bunu “kick” adında tek bir parça ve bir dizi Off adımı ile tanımlıyoruz. Eşzamansız veri almadığımız için, yan etkiler olmadan başlatmak için Cmd.none içeren komutları açıkça yok sayarız.

 init : ( Model, Cmd.Cmd Msg ) init = ( { track = { sequence = Array.initialize 16 (always Off) , name = "Kick" } } , Cmd.none )

İnit tipi ek açıklamamız programımıza uyuyor. Bu, sabit sayıda değer içeren tuple adı verilen bir veri yapısıdır. Bizim durumumuzda, Model ve komutlar. Şimdilik, daha sonra yan etkileri işlemeye hazır olana kadar Cmd.none kullanarak komutları her zaman yok sayarız. Uygulamamız hiçbir şey oluşturmaz, ancak derler!

Uygulamamızı Oluşturma

Görüşlerimizi oluşturalım. Bu noktada modelimizin tek bir izi var, yani render etmemiz gereken tek şey bu. HTML yapısı şöyle görünmelidir:

 <div class="track"> <p class "track-title">Kick</p> <div class="track-sequence"> <button class="step _active"></button> <button class="step"></button> <button class="step"></button> <button class="step"></button> etc... </div> </div>

Görüşlerimizi oluşturmak için üç işlev oluşturacağız:

  1. Parça adını ve sırayı içeren tek bir parçayı oluşturmak için
  2. Dizinin kendisini oluşturmak için başka bir
  3. Ve sıradaki her bir adım düğmesini oluşturmak için bir tane daha

İlk görüntüleme fonksiyonumuz tek bir iz oluşturacaktır. Geçilen tek bir izi zorlamak için tür açıklamamıza, renderTrack : Track -> Html Msg 'ye güveniyoruz. Türleri kullanmak, renderTrack bir izi olacağını her zaman bildiğimiz anlamına gelir. Kayıtta name alanının var olup olmadığını veya kayıt yerine bir dize geçip geçmediğimizi kontrol etmemize gerek yoktur. Track renderTrack başka bir şey geçirmeye çalışırsak Elm derlenmeyecektir. Daha da iyisi, eğer bir hata yaparsak ve yanlışlıkla fonksiyona bir iz dışında bir şey iletmeye çalışırsak, derleyici bize doğru yönü gösteren dostça mesajlar verecektir.

 renderTrack : Track -> Html Msg renderTrack track = div [ class "track" ] [ p [ class "track-title" ] [ text track.name ] , div [ class "track-sequence" ] (renderSequence track.sequence) ]

Açık görünebilir, ancak HTML yazmak da dahil olmak üzere tüm Elm Elm'dir. HTML yazmak için bir şablonlama dili veya soyutlama yoktur - hepsi Elm'dir. HTML öğeleri, adı, nitelik listesini ve alt öğelerin listesini alan Elm işlevleridir. Yani div [ class "track" ] [] çıktısı <div class="track"></div> . Listeler Elm'de virgülle ayrılır, bu nedenle div'e bir kimlik eklemek div [ class "track", id "my-id" ] [] gibi görünür.

Div kaydırma track-sequence , parçanın dizisini ikinci işlevimiz olan renderSequence . Bir dizi alır ve bir HTML düğmeleri listesi döndürür. Ek işlevi atlamak için renderSequence renderTrack tutabiliriz, ancak işlevleri daha küçük parçalara ayırmayı çok daha kolay buluyorum. Ek olarak, daha katı bir tür açıklama tanımlaması için başka bir fırsat elde ederiz.

 renderSequence : Array Step -> List (Html Msg) renderSequence sequence = Array.indexedMap renderStep sequence |> Array.toList

Sıradaki her adımın haritasını çıkarırız ve onu renderStep işlevine geçiririz. JavaScript eşlemesinde bir dizinle şu şekilde yazılır:

 sequence.map((node, index) => renderStep(index, node))

JavaScript ile karşılaştırıldığında, Elm'de eşleme neredeyse tersine çevrilir. İki bağımsız değişken alan Array.indexedMap : haritada uygulanacak işlev ( renderStep ) ve eşlenecek dizi ( sequence ). renderStep son işlevimizdir ve bir düğmenin etkin olup olmadığını belirler. indexedMap çünkü güncelleme fonksiyonuna geçirmek için adım indeksini (kimlik olarak kullandığımız) adımın kendisine aktarmamız gerekiyor.

 renderStep : Int -> Step -> Html Msg renderStep index step = let classes = if step == On then "step _active" else "step" in button [ class classes ] []

renderStep , dizini ilk argümanı, adımı ikinci argüman olarak kabul eder ve işlenmiş HTML'yi döndürür. Yerel işlevleri tanımlamak için bir let...in bloğu kullanarak, _active sınıfını On Steps'e atarız ve düğme özellikleri listesinde sınıf işlevimizi çağırırız.

Bir dizi adım içeren tekme izi
Bir dizi adım içeren tekme izi

Uygulama Durumunun Güncellenmesi

Bu noktada, uygulamamız kick dizisindeki 16 adımı işler, ancak tıklama adımı etkinleştirmez. Adım durumunu güncellemek için güncelleme fonksiyonuna bir mesaj ( Msg ) iletmemiz gerekiyor. Bunu, bir mesaj tanımlayarak ve bunu düğmemiz için bir olay işleyicisine ekleyerek yapıyoruz.

Types.elm'de ilk mesajımızı ToggleStep tanımlamamız gerekiyor. Sıra indeksi için bir Int ve bir Step alacaktır. Daha sonra, renderStep , argüman olarak sıra indeksi ve adım ile birlikte düğmenin tıklama olayına ToggleStep mesajını ekleriz. Bu, mesajı güncelleme işlevimize gönderecektir, ancak bu noktada güncelleme aslında hiçbir şey yapmayacaktır.

 type Msg = ToggleStep Int Step renderStep index step = let ... in button [ onClick (ToggleStep index step) , class classes ] []

Mesajlar normal türlerdir, ancak onları Elm'deki kural olan güncellemelere neden olan tür olarak tanımladık. Update.elm'de, model durumu değişikliklerini işlemek için Elm Mimarisini takip ediyoruz. Güncelleme işlevimiz bir Msg ve mevcut Model alacak ve yeni bir model ve potansiyel olarak bir komut döndürecektir. Komutlar, ikinci bölümde inceleyeceğimiz yan etkileri işler. Birden çok Msg olacağını biliyoruz, bu nedenle kalıp eşleştirme büyük/küçük harf bloğu oluşturduk. Bu, bizi tüm durumlarımızı ele alırken aynı zamanda durum akışını ayırmaya zorlar. Ve derleyici, modelimizi değiştirebilecek hiçbir durumu kaçırmadığımızdan emin olacaktır.

Elm'de bir kaydı güncellemek, JavaScript'te bir nesneyi güncellemekten biraz farklı yapılır. Kayıttaki bir alanı doğrudan record.field = * gibi değiştiremeyiz çünkü this veya self kullanamayız, ancak Elm'in yerleşik yardımcıları vardır. brian = { name = "brian" } gibi bir kayıt verildiğinde, ad alanını { brian | name = "BRIAN" } { brian | name = "BRIAN" } . Biçim şu şekildedir: { record | field = newValue } { record | field = newValue } .

Üst düzey alanları bu şekilde güncelleyebilirsiniz, ancak iç içe alanlar Elm'de daha aldatıcıdır. Kendi yardımcı işlevlerimizi tanımlamamız gerekiyor, bu nedenle iç içe geçmiş kayıtlara dalmak için dört yardımcı işlev tanımlayacağız:

  1. Adım değerini değiştirmek için bir
  2. Güncellenmiş adım değerini içeren yeni bir dizi döndürmek için bir tane
  3. Dizinin ait olduğu parçayı seçmek için başka bir
  4. Ve güncellenmiş adım değerini içeren güncellenmiş diziyi içeren yeni bir iz döndürmek için son bir işlev

İz dizisinin adım değerini Açık ve Kapalı arasında değiştirmek için ToggleStep ile başlıyoruz. Case deyiminde daha küçük işlevler yapmak için tekrar bir let...in bloğu kullanıyoruz. Adım zaten Kapalıysa, Açık yaparız ve bunun tersi de geçerlidir.

 toggleStep = if step == Off then On else Off

toggleStep , newSequence . İşlevsel dillerde veriler değişmezdir, bu nedenle diziyi değiştirmek yerine, aslında eskisini değiştirmek için güncellenmiş adım değerine sahip yeni bir dizi oluşturuyoruz.

 newSequence = Array.set index toggleStep selectedTrack.sequence

newSequence , değiştirmek istediğimiz dizini bulmak için Array.set kullanır ve ardından yeni diziyi oluşturur. Küme dizini bulamazsa, aynı sırayı döndürür. Hangi dizinin değiştirileceğini bilmek selectedTrack.sequence güvenir. selectedTrack , iç içe geçmiş kaydımıza ulaşabilmemiz için kullanılan temel yardımcı işlevimizdir. Bu noktada, şaşırtıcı derecede basit çünkü modelimizin sadece tek bir izi var.

 selectedTrack = model.track

Son yardımcı işlevimiz geri kalan her şeyi birbirine bağlar. Yine, veriler değişmez olduğundan, tüm parçamızı yeni bir dizi içeren yeni bir parça ile değiştiririz.

 newTrack = { selectedTrack | sequence = newSequence }

newTrack , görünümü yeniden oluşturan yeni izi içeren yeni bir model döndürdüğümüz let...in bloğunun dışında çağrılır. Yan etkileri geçmiyoruz, bu yüzden tekrar Cmd.none kullanıyoruz. Tüm update işlevimiz şöyle görünür:

 update : Msg -> Model -> ( Model, Cmd Msg ) update msg model = case msg of ToggleStep index step -> let selectedTrack = model.track newTrack = { selectedTrack | sequence = newSequence } toggleStep = if step == Off then On else Off newSequence = Array.set index toggleStep selectedTrack.sequence in ( { model | track = newTrack } , Cmd.none )

Programımızı çalıştırdığımızda, bir dizi adımdan oluşan işlenmiş bir iz görüyoruz. Adım düğmelerinden herhangi birine tıklamak, model durumunu değiştirmek için güncelleme işlevimize ToggleStep tetikler.

Bir dizi aktif adım içeren kick track
Bir dizi aktif adım içeren kick track

Uygulamamız ölçeklendikçe, Elm Architecture'ın tekrarlanabilir modelinin işleme durumunu nasıl basitleştirdiğini göreceğiz. Model, güncelleme ve görüntüleme işlevlerindeki aşinalık, iş alanımıza odaklanmamıza yardımcı olur ve başka birinin Elm uygulamasına atlamamızı kolaylaştırır.

Bir mola alarak

Yeni bir dilde yazmak zaman ve pratik gerektirir. Üzerinde çalıştığım ilk projeler, Elm sözdizimini, mimariyi ve işlevsel programlama paradigmalarını öğrenmek için kullandığım basit TypeForm klonlarıydı. Bu noktada, benzer bir şey yapmak için yeterince şey öğrendiniz. Eğer hevesliyseniz, Resmi Başlangıç ​​Kılavuzu'nu incelemenizi tavsiye ederim. Elm'in yaratıcısı Evan, pratik örnekler kullanarak Elm, sözdizimi, türler, Elm Mimarisi, ölçekleme ve daha fazlası için motivasyonlar konusunda size yol gösteriyor.

İkinci bölümde, Elm'in en iyi özelliklerinden birine dalacağız: adım sıralayıcımızı yeniden düzenlemek için derleyiciyi kullanmak. Ayrıca, yan etkiler için komutları kullanmayı ve JavaScript ile etkileşim kurmayı, yinelenen olayları nasıl ele alacağımızı öğreneceğiz. Bizi izlemeye devam edin!