F# Tutorial: Cara Membuat Aplikasi F# Full-stack
Diterbitkan: 2022-03-11Dalam beberapa tahun terakhir, pemrograman fungsional telah mendapatkan reputasi sebagai paradigma yang sangat ketat dan produktif. Tidak hanya bahasa pemrograman fungsional yang mendapatkan perhatian dalam komunitas programmer, tetapi banyak perusahaan besar juga mulai menggunakan bahasa pemrograman fungsional untuk memecahkan masalah komersial.
Misalnya, Walmart telah mulai menggunakan Clojure, dialek Lisp fungsional berbasis JVM, untuk infrastruktur checkoutnya; Jet.com, platform e-niaga besar (sekarang dimiliki oleh Walmart), menggunakan F# untuk membangun sebagian besar layanan mikronya; dan Jane Street, sebuah perusahaan perdagangan berpemilik, terutama menggunakan OCaml untuk membangun algoritme mereka.
Hari ini, kita akan menjelajahi pemrograman F#. F# adalah salah satu bahasa pemrograman fungsional yang mengalami peningkatan adopsi karena fleksibilitasnya, integrasi .NET yang kuat, dan alat yang tersedia berkualitas tinggi. Untuk keperluan tutorial F# ini, kami akan membangun server web sederhana dan aplikasi seluler terkait hanya menggunakan F# untuk ujung depan dan ujung belakang.
Mengapa Memilih F# dan Untuk Apa F# Digunakan?
Untuk proyek hari ini, kita hanya akan menggunakan F#. Ada beberapa alasan untuk memilih F# sebagai bahasa pilihan kami:
- Integrasi .NET: F# memiliki integrasi yang sangat erat dengan dunia .NET lainnya dan oleh karena itu akses siap ke ekosistem besar perpustakaan yang didukung dengan baik dan didokumentasikan secara menyeluruh untuk menyelesaikan berbagai tugas pemrograman.
- Ringkas: F# sangat ringkas karena sistem inferensi tipenya yang kuat dan sintaksis yang singkat. Tugas pemrograman seringkali dapat diselesaikan dengan lebih elegan menggunakan F# daripada C# atau Java. Kode F# dapat terlihat sangat ramping jika dibandingkan.
- Alat pengembang: F# menikmati integrasi kuat Visual Studio, yang merupakan salah satu IDE terbaik untuk ekosistem .NET. Bagi mereka yang bekerja pada platform non-Windows, ada banyak plugin dalam kode visual studio. Alat-alat ini membuat pemrograman di F# sangat produktif.
Saya dapat melanjutkan tentang manfaat menggunakan F#, tetapi tanpa basa-basi lagi, mari selami!
Ide Dibalik Tutorial F# kami
Di Amerika Serikat, ada pepatah populer: "Ini jam lima di suatu tempat" .
Di beberapa bagian dunia, pukul 17.00 adalah waktu paling awal ketika secara sosial dapat diterima untuk minum, atau secangkir teh tradisional.
Hari ini, kami akan membangun aplikasi berdasarkan konsep ini. Kami akan membangun aplikasi yang, pada waktu tertentu, mencari melalui berbagai zona waktu, mencari tahu di mana jam lima, dan memberikan informasi itu kepada pengguna.
Bagian Belakang
Menyiapkan Server Web
Kami akan mulai dengan membuat layanan back-end yang melakukan fungsi pencarian zona waktu. Kami akan menggunakan Suave.IO untuk membangun API JSON.
Suave.IO adalah kerangka kerja web intuitif dengan server web ringan yang memungkinkan aplikasi web sederhana dikodekan dengan sangat cepat.
Untuk memulai, buka Visual Studio dan mulai proyek Aplikasi Konsol F# baru. Jika opsi ini tidak tersedia untuk Anda, Anda mungkin perlu menginstal fungsionalitas F# dengan Visual Studio Installer. Beri nama proyek "FivePM". Setelah aplikasi Anda dibuat, Anda akan melihat sesuatu seperti ini:
[<EntryPoint>] let main argv = printfn "%A" argv 0 // return an integer exit code
Ini adalah bagian yang sangat sederhana dari kode starter yang mencetak argumen dan keluar dengan kode status 0. Jangan ragu untuk mengubah pernyataan cetak dan bereksperimen dengan berbagai fungsi kode. Pemformat “%A” adalah pemformat khusus yang mencetak representasi string dari jenis apa pun yang Anda masukkan, jadi jangan ragu untuk mencetak bilangan bulat, float, atau bahkan tipe kompleks. Setelah Anda merasa nyaman dengan sintaks dasar, saatnya untuk menginstal Suave.
Cara termudah untuk menginstal Suave adalah melalui manajer paket NuGet. Buka Proyek -> Kelola paket NuGet, dan klik tab jelajah. Cari Suave dan klik install. Setelah Anda menerima paket-paket yang akan diinstal, Anda seharusnya sudah siap! Sekarang kembali ke layar program.fs Anda dan kami siap untuk mulai membangun server.
Untuk mulai menggunakan Suave, kita perlu mengimpor paket terlebih dahulu. Di bagian atas program Anda, ketikkan pernyataan berikut:
open Suave open Suave.Operators open Suave.Filters open Suave.Successful
Ini akan mengimpor paket dasar yang diperlukan untuk membangun server web dasar. Sekarang ganti kode di utama dengan yang berikut ini, yang mendefinisikan aplikasi sederhana dan menyajikannya pada port 8080:
[<EntryPoint>] let main argv = // Define the port where you want to serve. We'll hardcode this for now. let port = 8080 // create an app config with the port let cfg = { defaultConfig with bindings = [ HttpBinding.createSimple HTTP "0.0.0.0" port]} // We'll define a single GET route at the / endpoint that returns "Hello World" let app = choose [ GET >=> choose [ path "/" >=> request (fun _ -> OK "Hello World!")] ] // Now we start the server startWebServer cfg app 0
Kode akan terlihat sangat mudah, bahkan jika Anda tidak terbiasa dengan sintaks F# atau cara Suave mendefinisikan penangan rute, kode tersebut harus cukup mudah dibaca. Pada dasarnya, aplikasi web kembali dengan status 200 dan "Halo Dunia!" string ketika dipukul dengan permintaan GET pada rute "/". Lanjutkan dan jalankan aplikasi (F5 di Visual Studio) dan navigasikan ke localhost:8080, dan Anda akan melihat "Hello World!" di jendela peramban Anda.
Memfaktorkan Ulang Kode Server
Sekarang kami memiliki server web! Sayangnya, itu tidak melakukan banyak hal - jadi mari kita berikan beberapa fungsionalitas! Pertama, mari kita pindahkan fungsionalitas server web ke tempat lain sehingga kita membangun beberapa fungsionalitas tanpa mengkhawatirkan server web (kita akan menghubungkannya ke server web nanti). Tentukan fungsi terpisah sebagai berikut:
// We'll use argv later :) let runWebServer argv = // Define the port where you want to serve. We'll hardcode this for now. let port = 8080 // create an app config with the port let cfg = { defaultConfig with bindings = [ HttpBinding.createSimple HTTP "0.0.0.0" port]} // We'll define a single GET route at the / endpoint that returns "Hello World" let app = choose [ GET >=> choose [ path "/" >=> request (fun _ -> OK "Hello World!")] ] // Now we start the server startWebServer cfg app
Sekarang ubah fungsi utama menjadi berikut dan pastikan kita melakukannya dengan benar.
[<EntryPoint>] let main argv = runWebServer argv 0
Tekan F5, dan "Halo Dunia!" kami! server harus berfungsi seperti sebelumnya.
Mendapatkan Zona Waktu
Sekarang mari kita bangun fungsionalitas yang menentukan zona waktu di mana jam lima. Kami ingin menulis beberapa kode untuk mengulangi semua zona waktu dan menentukan zona waktu yang paling dekat dengan 5:00 sore.
Selain itu, kami tidak benar-benar ingin mengembalikan zona waktu yang sangat dekat dengan 5:00 tetapi sedikit lebih awal (misalnya 16:58) karena, untuk tujuan demonstrasi ini, premisnya adalah tidak mungkin sebelum jam 5 sore. : 00 sore, betapapun dekat.
Mari kita mulai dengan mendapatkan daftar zona waktu. Dalam F# ini sangat mudah karena terintegrasi dengan baik dengan C#. Tambahkan "Sistem terbuka" di bagian atas, dan ubah aplikasi F# Anda menjadi ini:
[<EntryPoint>] let main argv = // This gets all the time zones into a List-like object let tzs = TimeZoneInfo.GetSystemTimeZones() // Now we iterate through the list and print out the names of the timezones for tz in tzs do printfn "%s" tz.DisplayName 0
Jalankan aplikasi dan Anda akan melihat di konsol Anda daftar semua zona waktu, offsetnya, dan nama tampilannya.
Membuat dan Menggunakan Jenis Kustom
Sekarang kita memiliki daftar zona waktu, kita dapat mengonversinya menjadi tipe data khusus yang lebih berguna bagi kita, sesuatu yang berisi informasi seperti offset UTC, waktu lokal, seberapa jauh jaraknya dari pukul 17:00 waktu setempat , dll. Untuk itu, mari kita tentukan jenis kustom, tepat di atas fungsi utama Anda:
type TZInfo = {tzName: string; minDiff: float; localTime: string; utcOffset: float}
Sekarang kita dapat mengubah dan informasi zona waktu yang kita dapatkan dari langkah terakhir ke dalam daftar objek TZInfo ini. Ubah fungsi utama Anda sebagai berikut:
[<EntryPoint>] let main argv = // This gets all the time zones into a List-like object let tzs = TimeZoneInfo.GetSystemTimeZones() // List comprehension + type inference allows us to easily perform conversions let tzList = [ for tz in tzs do // convert the current time to the local time zone let localTz = TimeZoneInfo.ConvertTime(DateTime.Now, tz) // Get the datetime object if it was 5:00pm let fivePM = DateTime(localTz.Year, localTz.Month, localTz.Day, 17, 0, 0) // Get the difference between now local time and 5:00pm local time. let minDifference = (localTz - fivePM).TotalMinutes yield { tzName=tz.StandardName; minDiff=minDifference; localTime=localTz.ToString("hh:mm tt"); utcOffset=tz.BaseUtcOffset.TotalHours; } ] printfn "%A" tzList.Head 0
Dan Anda akan melihat objek tzInfo untuk waktu Standar Dateline yang dicetak ke layar Anda.
Penyortiran dan Penyaringan dan Pemipaan, Astaga!
Sekarang kita memiliki daftar objek tzInfo ini, kita dapat memfilter dan mengurutkan objek ini untuk menemukan zona waktu di mana 1) setelah 5:00 sore dan 2) paling dekat dengan 5:00 sore dari zona waktu di 1). Ubah fungsi utama Anda seperti:
[<EntryPoint>] let main argv = // This gets all the time zones into a List-like object let tzs = TimeZoneInfo.GetSystemTimeZones() // List comprehension + type inference allows us to easily perform conversions let tzList = [ for tz in tzs do // convert the current time to the local time zone let localTz = TimeZoneInfo.ConvertTime(DateTime.Now, tz) // Get the datetime object if it was 5:00pm let fivePM = DateTime(localTz.Year, localTz.Month, localTz.Day, 17, 0, 0) // Get the difference between now local time and 5:00pm local time. let minDifference = (localTz - fivePM).TotalMinutes yield { tzName=tz.StandardName; minDiff=minDifference; localTime=localTz.ToString("hh:mm tt"); utcOffset=tz.BaseUtcOffset.TotalHours; } ] // We use the pipe operator to chain functiona calls together let closest = tzList // filter so that we only get tz after 5pm |> List.filter (fun (i:TZInfo) -> i.minDiff >= 0.0) // sort by minDiff |> List.sortBy (fun (i:TZInfo) -> i.minDiff) // Get the first item |> List.head printfn "%A" closest
Dan sekarang kita harus memiliki zona waktu yang kita cari.
Memfaktorkan Ulang Pengambil Zona Waktu ke Fungsinya Sendiri
Sekarang mari kita refactor kode ke fungsinya sendiri sehingga kita bisa menggunakannya nanti. Definisikan sebuah fungsi sebagai berikut:
// the function takes uint as input, and we represent that as "()" let getClosest () = // This gets all the time zones into a List-like object let tzs = TimeZoneInfo.GetSystemTimeZones() // List comprehension + type inference allows us to easily perform conversions let tzList = [ for tz in tzs do // convert the current time to the local time zone let localTz = TimeZoneInfo.ConvertTime(DateTime.Now, tz) // Get the datetime object if it was 5:00pm let fivePM = DateTime(localTz.Year, localTz.Month, localTz.Day, 17, 0, 0) // Get the difference between now local time and 5:00pm local time. let minDifference = (localTz - fivePM).TotalMinutes yield { tzName=tz.StandardName; minDiff=minDifference; localTime=localTz.ToString("hh:mm tt"); utcOffset=tz.BaseUtcOffset.TotalHours; } ] // We use the pipe operator to chain function calls together tzList // filter so that we only get tz after 5pm |> List.filter (fun (i:TZInfo) -> i.minDiff >= 0.0) // sort by minDiff |> List.sortBy (fun (i:TZInfo) -> i.minDiff) // Get the first item |> List.head And our main function can just be: [<EntryPoint>] let main argv = printfn "%A" <| getClosest() 0
Jalankan kode dan Anda akan melihat output yang sama seperti sebelumnya.

Pengkodean JSON Data Pengembalian
Sekarang kita bisa mendapatkan data zona waktu, kita dapat mengubah informasi menjadi JSON dan menyajikannya melalui aplikasi kita. Ini cukup sederhana, berkat paket JSON.NET dari NewtonSoft. Kembali ke manajer paket NuGet Anda dan temukan Newtonsoft.Json, dan instal paketnya. Sekarang kembali ke Program.fs dan buat sedikit perubahan pada fungsi utama kita:
[<EntryPoint>] let main argv = printfn "%s" <| JsonConvert.SerializeObject(getClosest()) 0
Jalankan kode sekarang dan alih-alih objek TZInfo, Anda akan melihat JSON dicetak ke konsol Anda.
Menghubungkan Info Zona Waktu ke JSON API
Sangat mudah untuk menghubungkan ini ke JSON API kami. Cukup buat perubahan berikut pada fungsi runWebServer Anda:
// We'll use argv later :) let runWebServer argv = // Define the port where you want to serve. We'll hardcode this for now. let port = 8080 // create an app config with the port let cfg = { defaultConfig with bindings = [ HttpBinding.createSimple HTTP "0.0.0.0" port]} // We'll define a single GET route at the / endpoint that returns "Hello World" let app = choose [ GET >=> choose [ // We are getting the closest time zone, converting it to JSON, then setting the MimeType path "/" >=> request (fun _ -> OK <| JsonConvert.SerializeObject(getClosest())) >=> setMimeType "application/json; charset=utf-8" ] ] // Now we start the server startWebServer cfg app
Jalankan aplikasi, dan arahkan ke localhost:8080. Anda akan melihat JSON di jendela browser Anda.
Menyebarkan Server
Sekarang kami memiliki server JSON API, kami dapat menerapkannya sehingga dapat diakses di internet. Salah satu cara termudah untuk menyebarkan aplikasi ini adalah melalui Layanan Aplikasi Microsoft Azure yang dapat dipahami sebagai layanan IIS terkelola. Untuk menyebarkan ke layanan Aplikasi Azure, buka https://portal.azure.com dan buka Layanan Aplikasi. Buat aplikasi baru, dan navigasikan ke pusat penerapan di portal Anda. Portal ini bisa menjadi sedikit berlebihan jika Anda baru pertama kali melakukannya, jadi jika Anda mengalami kesulitan pastikan untuk berkonsultasi dengan salah satu dari banyak tutorial di luar sana untuk menggunakan Layanan Aplikasi.
Anda akan melihat berbagai opsi untuk penerapan. Anda dapat menggunakan apa pun yang Anda suka, tetapi demi kesederhanaan, kami dapat menggunakan opsi FTP.
Layanan Aplikasi mencari file web.config di akar aplikasi Anda untuk mengetahui cara menjalankan aplikasi Anda. Karena server web kami adalah aplikasi konsol yang penting, kami dapat memublikasikan aplikasi dan berintegrasi dengan server IIS menggunakan HttpPlatformHandler. Di Visual Studio, tambahkan file XML ke proyek Anda dan beri nama web.config. Isi dengan XML berikut:
<?xml version="1.0" encoding="UTF-8"?> <configuration> <system.webServer> <handlers> <remove name="httpplatformhandler" /> <add name="httpplatformhandler" path="*" verb="*" modules="httpPlatformHandler" resourceType="Unspecified"/> </handlers> <httpPlatform stdoutLogEnabled="true" stdoutLogFile="suave.log" startupTimeLimit="20" processPath=".\publish\FivePM.exe" arguments="%HTTP_PLATFORM_PORT%"/> </system.webServer> </configuration>
Hubungkan ke server FTP menggunakan kredensial yang diperoleh dari pusat penyebaran (Anda harus mengklik opsi FTP). Pindahkan web.config ke folder wwwroot situs FTP layanan aplikasi Anda.
Sekarang kita ingin membangun dan memublikasikan aplikasi kita, tetapi sebelum melakukannya, kita perlu membuat sedikit perubahan pada kode server. Buka fungsi runServer Anda dan ubah 3 baris pertama menjadi yang berikut:
let runWebServer (argv:string[]) = // Define the port where you want to serve. We'll hardcode this for now. let port = if argv.Length = 0 then 8080 else (int argv.[0])
Yang memungkinkan aplikasi untuk melihat argumen yang diteruskan dan menggunakan argumen pertama sebagai nomor port daripada membuat port dikodekan ke 8080. Dalam konfigurasi web, kami meneruskan %HTTP_PLATFORM_PORT% sebagai argumen pertama, jadi kami harus ditetapkan.
Bangun aplikasi dalam mode rilis, publikasikan aplikasi, dan salin folder yang diterbitkan ke wwwroot. Mulai ulang aplikasi dan Anda akan melihat hasil JSON API di situs *.azurewebsites.net
.
Sekarang aplikasi kita dikerahkan!
Bagian Depan
Sekarang setelah server di-deploy, kita bisa membangun front end. Untuk front end, kita akan membangun sebuah aplikasi Android menggunakan Xamarin dan F#. Tumpukan ini, seperti lingkungan back-end kami, menikmati integrasi mendalam dengan Visual Studio. Tentu saja, ekosistem F# mendukung beberapa opsi pengembangan front-end (WebSharper, Fable/Elmish, Xamarin.iOS, DotLiquid dll), tetapi demi singkatnya, kami hanya akan mengembangkan menggunakan Xamarin.Android untuk posting ini dan pergi mereka untuk posting masa depan.
Pengaturan
Untuk mengatur aplikasi Android, mulai proyek baru dan pilih opsi Xamarin Android. Pastikan Anda telah menginstal alat pengembangan Android. Setelah proyek diatur, Anda akan melihat sesuatu seperti ini di file kode utama Anda.
[<Activity (Label = "FivePMFinder", MainLauncher = true, Icon = "@mipmap/icon")>] type MainActivity () = inherit Activity () let mutable count:int = 1 override this.OnCreate (bundle) = base.OnCreate (bundle) // Set our view from the "main" layout resource this.SetContentView (Resources.Layout.Main) // Get our button from the layout resource, and attach an event to it let button = this.FindViewById<Button>(Resources.Id.myButton) button.Click.Add (fun args -> button.Text <- sprintf "%d clicks!" count count <- count + 1 )
Ini adalah kode starter untuk F# Android Xamarin. Kode saat ini hanya melacak berapa kali tombol diklik dan menampilkan nilai hitungan saat ini. Anda dapat melihatnya bekerja dengan menekan F5 untuk meluncurkan emulator dan memulai aplikasi dalam mode debug.
Menambahkan Komponen UI
Mari tambahkan beberapa komponen UI dan membuatnya lebih berguna. Buka sumber daya/tata letak dan navigasikan ke Main.axml.
Anda akan melihat representasi visual dari tata letak aktivitas utama. Anda dapat mengedit berbagai elemen UI dengan mengklik elemen tersebut. Anda dapat menambahkan elemen dengan masuk ke kotak alat dan memilih elemen yang ingin Anda tambahkan. Ganti nama tombol dan tambahkan textView di bawah tombol. Representasi XML dari AXML Anda akan terlihat seperti ini:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:andro android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <Button android: android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/fivePM" /> <TextView android:text="" android:textAppearance="?android:attr/textAppearanceMedium" android:layout_width="match_parent" android:layout_height="wrap_content" android: /> </LinearLayout>
AXML membuat referensi ke file sumber daya string, jadi buka resources/values/strings.xml Anda dan buat perubahan berikut:
<?xml version="1.0" encoding="utf-8"?> <resources> <string name="fivePM">It\'s 5PM Somewhere!</string> <string name="app_name">5PM Finder</string> </resources>
Sekarang kami telah membangun AXML front-end. Sekarang mari kita hubungkan ke beberapa kode. Arahkan ke MainActivity.fs dan buat perubahan berikut pada fungsi onCreate Anda:
base.OnCreate (bundle) // Set our view from the "main" layout resource this.SetContentView (Resources.Layout.Main) // Get our button from the layout resource, and attach an event to it let button = this.FindViewById<Button>(Resources.Id.myButton) let txtView = this.FindViewById<TextView>(Resources.Id.textView1); button.Click.Add (fun args -> let webClient = new WebClient() txtView.Text <- webClient.DownloadString("https://fivepm.azurewebsites.net/") )
Ganti fivepm.azurewebsites.net dengan URL penerapan JSON API Anda sendiri. Jalankan aplikasi dan klik tombol di emulator. Sebentar lagi, Anda akan melihat JSON API kembali dengan hasil API Anda.
Mengurai JSON
Kami hampir sampai! Saat ini, aplikasi kami menampilkan JSON mentah dan agak tidak terbaca. Langkah selanjutnya adalah mengurai JSON dan mengeluarkan string yang lebih dapat dibaca manusia. Untuk mengurai JSON, kita bisa menggunakan library Newtonsoft.JSON dari server.
Arahkan ke manajer paket NuGet Anda dan cari Newtonsoft.JSON. Instal dan kembali ke file MainActivity.fs. Impor dengan menambahkan "buka Newtonsoft.Json".
Sekarang tambahkan tipe TZInfo ke proyek. Kami dapat menggunakan kembali TZInfo dari server, tetapi karena kami sebenarnya hanya membutuhkan dua bidang, kami dapat menentukan jenis khusus di sini:
type TZInfo = { tzName:string localTime: string }
Tambahkan definisi tipe di atas fungsi utama, dan sekarang ubah fungsi utama sebagai berikut:
button.Click.Add (fun args -> let webClient = new WebClient() let tzi = JsonConvert.DeserializeObject<TZInfo>(webClient.DownloadString("https://fivepm.azurewebsites.net/")) txtView.Text <- sprintf "It's (about) 5PM in the\n\n%s Timezone! \n\nSpecifically, it is %s there" tzi.tzName tzi.localTime )
Sekarang hasil JSON API dideserialisasi ke objek TZInfo dan digunakan untuk membuat string. Jalankan aplikasi dan klik tombol. Anda akan melihat string yang diformat muncul di layar.
Meskipun aplikasi ini sangat sederhana dan mungkin tidak disempurnakan, kami telah membangun aplikasi seluler yang menggunakan dan F# JSON API, mengubah data, dan menampilkannya kepada pengguna. Dan kami melakukan semuanya di F#. Jangan ragu untuk bermain-main dengan berbagai kontrol dan lihat apakah Anda dapat meningkatkan desainnya.
Dan di sana kita memilikinya! Aplikasi seluler F# sederhana dan API F# JSON dan memberi tahu pengguna di mana saat ini pukul lima.
Membungkus
Hari ini kita berjalan melalui pembuatan Web API sederhana dan aplikasi Android sederhana hanya dengan menggunakan F#, yang menunjukkan ekspresif bahasa F# dan kekuatan ekosistem F#. Namun, kami baru saja menggores permukaan pengembangan F#, jadi saya akan menulis beberapa posting lagi untuk membangun apa yang telah kita diskusikan hari ini. Selain itu, saya harap postingan ini menginspirasi Anda untuk membuat aplikasi F# Anda sendiri!
Anda dapat menemukan kode yang kami gunakan untuk tutorial ini di GitHub.