บทช่วยสอน F#: วิธีสร้างแอป F# แบบเต็มสแต็ก

เผยแพร่แล้ว: 2022-03-11

ในช่วงไม่กี่ปีที่ผ่านมา การเขียนโปรแกรมเชิงฟังก์ชันได้รับชื่อเสียงว่าเป็นกระบวนทัศน์ที่เข้มงวดและมีประสิทธิผลเป็นพิเศษ ไม่เพียงแต่ภาษาโปรแกรมที่ใช้งานได้เท่านั้นที่ดึงดูดความสนใจภายในชุมชนโปรแกรมเมอร์ แต่บริษัทขนาดใหญ่หลายแห่งก็เริ่มใช้ภาษาโปรแกรมเชิงฟังก์ชันเพื่อแก้ปัญหาเชิงพาณิชย์ด้วย

ตัวอย่างเช่น Walmart ได้เริ่มใช้ Clojure ซึ่งเป็นภาษา Lisp ที่ทำงานบน JVM สำหรับโครงสร้างพื้นฐานการชำระเงิน Jet.com ซึ่งเป็นแพลตฟอร์มอีคอมเมิร์ซขนาดใหญ่ (ปัจจุบันเป็นของ Walmart) ใช้ F# เพื่อสร้างไมโครเซอร์วิสส่วนใหญ่ และ Jane Street ซึ่งเป็นบริษัทการค้าที่มีกรรมสิทธิ์ ส่วนใหญ่ใช้ OCaml เพื่อสร้างอัลกอริธึมของพวกเขา

วันนี้เราจะมาสำรวจการเขียนโปรแกรม F# F# เป็นหนึ่งในภาษาโปรแกรมที่ใช้งานได้จริงซึ่งมีการนำไปใช้เพิ่มขึ้นเนื่องจากความยืดหยุ่น การรวม .NET ที่แข็งแกร่ง และเครื่องมือคุณภาพสูงที่พร้อมใช้งาน สำหรับวัตถุประสงค์ของบทช่วยสอน F# นี้ เราจะสร้างเว็บเซิร์ฟเวอร์อย่างง่ายและแอปมือถือที่เกี่ยวข้องโดยใช้ F# เท่านั้นสำหรับทั้งส่วนหน้าและส่วนหลัง

ทำไมต้องเลือก F# และ F# ใช้สำหรับอะไร?

สำหรับโครงการวันนี้ เราจะไม่ใช้อะไรนอกจาก F# มีเหตุผลหลายประการที่ชอบใช้ F# เป็นภาษาที่เราเลือก:

  • การรวม .NET: F# มีการผสานรวมกับส่วนอื่นๆ ของโลก .NET อย่างแน่นหนา ดังนั้นจึงพร้อมเข้าถึงระบบนิเวศขนาดใหญ่ของไลบรารีที่ได้รับการสนับสนุนอย่างดีและมีเอกสารอย่างถี่ถ้วนสำหรับการแก้ปัญหางานเขียนโปรแกรมที่หลากหลาย
  • ความ รัดกุม: F# มีความกระชับอย่างยิ่งเนื่องจากระบบการอนุมานประเภทที่มีประสิทธิภาพและไวยากรณ์ที่กระชับ งานเขียนโปรแกรมมักจะแก้ไขได้อย่างสวยงามโดยใช้ F# มากกว่า C# หรือ Java รหัส F# อาจดูคล่องตัวมากเมื่อเปรียบเทียบ
  • เครื่องมือสำหรับนักพัฒนา: F# มีการรวม Visual Studio ที่แข็งแกร่ง ซึ่งเป็นหนึ่งใน IDE ที่ดีที่สุดสำหรับระบบนิเวศ .NET สำหรับผู้ที่ทำงานบนแพลตฟอร์มที่ไม่ใช่ Windows มีปลั๊กอินมากมายในโค้ด Visual Studio เครื่องมือเหล่านี้ทำให้การเขียนโปรแกรมใน F# มีประสิทธิภาพสูงสุด

ฉันสามารถพูดถึงประโยชน์ของการใช้ F# ต่อไปได้ แต่โดยไม่ต้องกังวลใจอีกต่อไป มาดำดิ่งกัน!

แนวคิดเบื้องหลังการสอน F# ของเรา

ในสหรัฐอเมริกา มีคำกล่าวที่นิยมว่า “ที่ไหนสักแห่งเวลาห้าโมงเย็น” .

ในบางส่วนของโลก เวลา 17.00 น. เป็นช่วงเวลาที่เร็วที่สุดที่สังคมยอมรับในการดื่มหรือดื่มชาแบบดั้งเดิม

วันนี้เราจะสร้างแอปพลิเคชันตามแนวคิดนี้ เราจะสร้างแอปพลิเคชันที่ค้นหาโซนเวลาต่างๆ ตามเวลาที่กำหนด ค้นหาว่าตอนนี้อยู่ที่ไหน และให้ข้อมูลนั้นแก่ผู้ใช้

The Back End

การตั้งค่าเว็บเซิร์ฟเวอร์

เราจะเริ่มต้นด้วยการสร้างบริการแบ็คเอนด์ที่ทำหน้าที่ค้นหาเขตเวลา เราจะใช้ Suave.IO เพื่อสร้าง JSON API

ภาพประกอบการสอน F#: การตั้งค่าเว็บเซิร์ฟเวอร์

Suave.IO เป็นเว็บเฟรมเวิร์กที่ใช้งานง่ายพร้อมเว็บเซิร์ฟเวอร์น้ำหนักเบาที่ช่วยให้เว็บแอปอย่างง่ายสามารถเขียนโค้ดได้อย่างรวดเร็ว

ในการเริ่มต้น ให้ไปที่ Visual Studio และเริ่มโครงการแอปพลิเคชันคอนโซล F# ใหม่ หากไม่มีตัวเลือกนี้ คุณอาจต้องติดตั้งฟังก์ชัน F# ด้วย Visual Studio Installer ตั้งชื่อโครงการ “FivePM” เมื่อสร้างแอปพลิเคชันของคุณแล้ว คุณควรเห็นสิ่งนี้:

 [<EntryPoint>] let main argv = printfn "%A" argv 0 // return an integer exit code

นี่เป็นโค้ดเริ่มต้นง่ายๆ ที่พิมพ์อาร์กิวเมนต์และออกด้วยรหัสสถานะ 0 คุณสามารถเปลี่ยนคำสั่งการพิมพ์และทดลองกับฟังก์ชันต่างๆ ของโค้ดได้ตามสบาย ตัวจัดรูปแบบ “%A” เป็นรูปแบบพิเศษที่พิมพ์การแสดงสตริงของประเภทใดก็ตามที่คุณส่ง ดังนั้นอย่าลังเลที่จะพิมพ์จำนวนเต็ม ทุ่น หรือแม้แต่ประเภทที่ซับซ้อน เมื่อคุณคุ้นเคยกับรูปแบบพื้นฐานแล้ว ก็ถึงเวลาติดตั้ง Suave

วิธีที่ง่ายที่สุดในการติดตั้ง Suave คือการใช้ตัวจัดการแพ็คเกจ NuGet ไปที่โครงการ -> จัดการแพ็คเกจ NuGet และคลิกที่แท็บเรียกดู ค้นหา Suave แล้วคลิกติดตั้ง เมื่อคุณยอมรับแพ็คเกจที่จะติดตั้ง คุณก็พร้อมแล้ว! กลับไปที่หน้าจอ program.fs ของคุณ และเราพร้อมที่จะเริ่มสร้างเซิร์ฟเวอร์แล้ว

เพื่อเริ่มใช้ Suave เราจะต้องนำเข้าแพ็คเกจก่อน ที่ด้านบนของโปรแกรม ให้พิมพ์ข้อความต่อไปนี้:

 open Suave open Suave.Operators open Suave.Filters open Suave.Successful

สิ่งนี้จะนำเข้าแพ็คเกจพื้นฐานที่จำเป็นในการสร้างเว็บเซิร์ฟเวอร์พื้นฐาน ตอนนี้แทนที่โค้ดใน main ด้วยสิ่งต่อไปนี้ซึ่งกำหนดแอปอย่างง่ายและให้บริการบนพอร์ต 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

โค้ดควรดูตรงไปตรงมามาก แม้ว่าคุณจะไม่คุ้นเคยกับไวยากรณ์ F# หรือวิธีกำหนดตัวจัดการเส้นทางของ Suave โค้ดก็ควรอ่านได้ค่อนข้างดี โดยพื้นฐานแล้ว เว็บแอปจะกลับมาพร้อมสถานะ 200 และ "สวัสดีชาวโลก!" สตริงเมื่อถูกกดด้วยคำขอ GET บนเส้นทาง "/" ไปข้างหน้าและเรียกใช้แอปพลิเคชัน (F5 ใน Visual Studio) และไปที่ localhost:8080 และคุณจะเห็น "Hello World!" ในหน้าต่างเบราว์เซอร์ของคุณ

การปรับโครงสร้างรหัสเซิร์ฟเวอร์

ตอนนี้เรามีเว็บเซิร์ฟเวอร์แล้ว! น่าเสียดายที่มันไม่ได้ทำอะไรมากมาย - ดังนั้นให้ฟังก์ชั่นบางอย่าง! ขั้นแรก ให้ย้ายฟังก์ชันการทำงานของเว็บเซิร์ฟเวอร์ไปที่อื่น เพื่อให้เราสร้างฟังก์ชันการทำงานบางอย่างโดยไม่ต้องกังวลเกี่ยวกับเว็บเซิร์ฟเวอร์ (เราจะเชื่อมต่อกับเว็บเซิร์ฟเวอร์ในภายหลัง) กำหนดฟังก์ชันแยกต่างหากดังนี้:

 // 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

ตอนนี้เปลี่ยนฟังก์ชั่นหลักดังต่อไปนี้และตรวจสอบให้แน่ใจว่าเราทำถูกต้อง

 [<EntryPoint>] let main argv = runWebServer argv 0

กด F5 และ "Hello World!" ของเรา เซิร์ฟเวอร์ควรทำงานเหมือนเมื่อก่อน

รับเขตเวลา

ตอนนี้ เรามาสร้างฟังก์ชันการทำงานที่กำหนดเขตเวลาซึ่งเป็นเวลาห้าโมงเย็นกัน เราต้องการเขียนโค้ดเพื่อวนซ้ำในเขตเวลาทั้งหมดและกำหนดเขตเวลาที่ใกล้เคียงที่สุดกับ 17.00 น.

กำลังดึงภาพประกอบโซนเวลา

ยิ่งกว่านั้น เราไม่ต้องการคืนเขตเวลาที่ใกล้กับ 17.00 น. มากนัก แต่ก่อนนั้นเล็กน้อย (เช่น 16:58 น.) เพราะสำหรับจุดประสงค์ของการสาธิตนี้ ข้อสันนิษฐานคือต้องไม่อยู่ก่อน 5 โมงเย็น 00:00 น. แต่ใกล้ถึงแล้ว

เริ่มต้นด้วยการรับรายการเขตเวลา ใน F# นี่เป็นเรื่องง่ายมากเนื่องจากสามารถทำงานร่วมกับ C# ได้เป็นอย่างดี เพิ่ม "open System" ที่ด้านบนและเปลี่ยนแอปพลิเคชัน F# ของคุณเป็น:

 [<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

เรียกใช้แอปพลิเคชันและคุณจะเห็นรายการโซนเวลา ออฟเซ็ต และชื่อที่แสดงในคอนโซลของคุณ

การสร้างและการใช้ Custom Type

ตอนนี้เรามีรายการเขตเวลาแล้ว เราสามารถแปลงให้เป็นประเภทข้อมูลที่กำหนดเองซึ่งเป็นประโยชน์กับเรามากขึ้น บางอย่างที่มีข้อมูล เช่น ออฟเซ็ต UTC เวลาท้องถิ่น ตั้งแต่เวลา 17.00 น. ตามเวลาท้องถิ่น ฯลฯ เพื่อจุดประสงค์นี้ มากำหนดประเภทที่กำหนดเอง เหนือฟังก์ชันหลักของคุณ:

 type TZInfo = {tzName: string; minDiff: float; localTime: string; utcOffset: float}

ตอนนี้เราสามารถแปลงและข้อมูลเขตเวลาที่เราได้รับจากขั้นตอนสุดท้ายลงในรายการของวัตถุ TZInfo นี้ เปลี่ยนฟังก์ชั่นหลักของคุณดังนี้:

 [<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

และคุณควรเห็นวัตถุ tzInfo สำหรับเวลามาตรฐานของ Dateline ที่พิมพ์ลงบนหน้าจอของคุณ

การคัดแยกและการกรองและการวางท่อ โอ้ มาย!

ตอนนี้เรามีรายการของอ็อบเจ็กต์ tzInfo เหล่านี้แล้ว เราสามารถกรองและจัดเรียงออบเจ็กต์เหล่านี้เพื่อค้นหาเขตเวลาที่ 1) หลัง 17.00 น. และ 2) ใกล้เคียงที่สุดถึง 17.00 น. ของเขตเวลาใน 1) เปลี่ยนฟังก์ชั่นหลักของคุณดังนี้:

 [<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

และตอนนี้เราควรจะมีเขตเวลาที่เรากำลังมองหา

การปรับโครงสร้าง Timezone Getter เป็นฟังก์ชันของตัวเอง

ตอนนี้ เรามาปรับโครงสร้างโค้ดใหม่ให้เป็นฟังก์ชันของตัวเองกัน เพื่อให้เราสามารถใช้งานได้ในภายหลัง กำหนดฟังก์ชันดังนี้:

 // 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

รันโค้ดแล้วคุณจะเห็นผลลัพธ์เหมือนเดิม

JSON เข้ารหัสข้อมูลส่งคืน

ตอนนี้เราสามารถรับข้อมูลเขตเวลาได้แล้ว เราจึงสามารถแปลงข้อมูลเป็น JSON และให้บริการผ่านแอปพลิเคชันของเราได้ มันค่อนข้างง่าย ขอบคุณแพ็คเกจ JSON.NET จาก NewtonSoft กลับไปที่ตัวจัดการแพ็คเกจ NuGet ของคุณและค้นหา Newtonsoft.Json และติดตั้งแพ็คเกจ กลับไปที่ Program.fs และทำการเปลี่ยนแปลงเล็กน้อยในฟังก์ชันหลักของเรา:

 [<EntryPoint>] let main argv = printfn "%s" <| JsonConvert.SerializeObject(getClosest()) 0

รันโค้ดทันทีและแทนที่จะใช้อ็อบเจ็กต์ TZInfo คุณควรเห็น JSON พิมพ์ไปที่คอนโซลของคุณ

การเชื่อมต่อข้อมูลโซนเวลากับ JSON API

การเชื่อมต่อสิ่งนี้กับ JSON API ของเรานั้นง่ายมาก เพียงทำการเปลี่ยนแปลงต่อไปนี้กับฟังก์ชัน runWebServer ของคุณ:

 // 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

เรียกใช้แอปพลิเคชันและไปที่ localhost:8080 คุณควรเห็น JSON บนหน้าต่างเบราว์เซอร์ของคุณ

การปรับใช้เซิร์ฟเวอร์

ตอนนี้เรามีเซิร์ฟเวอร์ JSON API แล้ว เราก็ปรับใช้เพื่อให้สามารถเข้าถึงได้บนอินเทอร์เน็ต วิธีที่ง่ายที่สุดวิธีหนึ่งในการปรับใช้แอปพลิเคชันนี้คือผ่าน App Service ของ Microsoft Azure ซึ่งสามารถเข้าใจได้ว่าเป็นบริการ IIS ที่มีการจัดการ ในการปรับใช้กับบริการ Azure App ตรงไปที่ https://portal.azure.com และไปที่ App Service สร้างแอปใหม่และไปที่ศูนย์การปรับใช้ในพอร์ทัลของคุณ พอร์ทัลอาจดูล้นหลามเล็กน้อยหากเป็นครั้งแรกของคุณ ดังนั้นหากคุณมีปัญหา โปรดปรึกษาหนึ่งในบทช่วยสอนมากมายสำหรับการใช้บริการแอพ

คุณควรเห็นตัวเลือกต่างๆ สำหรับการปรับใช้ คุณสามารถใช้อะไรก็ได้ที่คุณชอบ แต่เพื่อความง่าย เราสามารถใช้ตัวเลือก FTP ได้

บริการแอพจะค้นหาไฟล์ web.config ที่รูทของแอปพลิเคชันของคุณเพื่อทราบวิธีเรียกใช้แอปพลิเคชันของคุณ เนื่องจากเว็บเซิร์ฟเวอร์ของเราเป็นแอปพลิเคชันคอนโซลที่จำเป็น เราจึงสามารถเผยแพร่แอปพลิเคชันและรวมเข้ากับเซิร์ฟเวอร์ IIS โดยใช้ HttpPlatformHandler ใน Visual Studio เพิ่มไฟล์ XML ในโครงการของคุณและตั้งชื่อเป็น web.config กรอก XML ต่อไปนี้:

 <?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>

เชื่อมต่อกับเซิร์ฟเวอร์ FTP โดยใช้ข้อมูลประจำตัวที่ได้รับจากศูนย์ปรับใช้ (คุณจะต้องคลิกที่ตัวเลือก FTP) ย้าย web.config ไปยังโฟลเดอร์ wwwroot ของไซต์ FTP ของบริการแอปของคุณ

ตอนนี้ เราต้องการสร้างและเผยแพร่แอปพลิเคชันของเรา แต่ก่อนที่เราจะทำ เราจำเป็นต้องทำการเปลี่ยนแปลงเล็กน้อยในโค้ดเซิร์ฟเวอร์ ไปที่ฟังก์ชัน runServer ของคุณและเปลี่ยน 3 บรรทัดแรกดังต่อไปนี้:

 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])

ซึ่งช่วยให้แอปพลิเคชันสามารถดูอาร์กิวเมนต์ที่ส่งผ่านและใช้อาร์กิวเมนต์แรกเป็นหมายเลขพอร์ตแทนที่จะให้พอร์ตถูกฮาร์ดโค้ดเป็น 8080 ในการกำหนดค่าเว็บเราส่ง %HTTP_PLATFORM_PORT% เป็นอาร์กิวเมนต์แรกดังนั้นเราจึง ควรตั้งค่า

สร้างแอปพลิเคชันในโหมดเผยแพร่ เผยแพร่แอปพลิเคชัน และคัดลอกโฟลเดอร์ที่เผยแพร่ไปยัง wwwroot รีสตาร์ทแอปพลิเคชันและคุณควรเห็นผลลัพธ์ JSON API ที่ไซต์ *.azurewebsites.net

ตอนนี้แอปพลิเคชันของเราใช้งานได้แล้ว!

The Front End

F# ภาพประกอบส่วนหน้า

ตอนนี้เราได้ปรับใช้เซิร์ฟเวอร์แล้ว เราสามารถสร้างส่วนหน้าได้ สำหรับส่วนหน้า เราจะสร้างแอปพลิเคชัน Android โดยใช้ Xamarin และ F# สแต็กนี้ เช่นเดียวกับสภาพแวดล้อมแบ็คเอนด์ของเรา สนุกกับการรวมเข้ากับ Visual Studio อย่างลึกซึ้ง แน่นอน ระบบนิเวศ F# รองรับตัวเลือกการพัฒนาส่วนหน้าค่อนข้างน้อย (WebSharper, Fable/Elmish, Xamarin.iOS, DotLiquid เป็นต้น) แต่เพื่อความกระชับ เราจะพัฒนาโดยใช้ Xamarin.Android เท่านั้นสำหรับโพสต์นี้และจากไป สำหรับโพสต์ในอนาคต

การตั้งค่า

ในการตั้งค่าแอพ Android ให้เริ่มโครงการใหม่และเลือกตัวเลือก Xamarin Android ตรวจสอบให้แน่ใจว่าคุณได้ติดตั้งเครื่องมือพัฒนา Android แล้ว เมื่อสร้างโปรเจ็กต์แล้ว คุณควรเห็นสิ่งนี้ในไฟล์โค้ดหลักของคุณ

 [<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 )

นี่คือรหัสเริ่มต้นสำหรับ F# Android Xamarin โค้ดในปัจจุบันเป็นเพียงการติดตามจำนวนครั้งที่คลิกปุ่มและแสดงค่าการนับปัจจุบัน คุณสามารถดูการทำงานได้โดยการกด F5 เพื่อเปิดใช้โปรแกรมจำลองและเริ่มแอปพลิเคชันในโหมดดีบัก

การเพิ่มส่วนประกอบ UI

มาเพิ่มองค์ประกอบ UI บางอย่างและทำให้มีประโยชน์มากขึ้น เปิดทรัพยากร/เลย์เอาต์และไปที่ Main.axml

คุณควรเห็นการแสดงภาพเค้าโครงกิจกรรมหลัก คุณสามารถแก้ไของค์ประกอบ UI ต่างๆ ได้โดยคลิกที่องค์ประกอบ คุณสามารถเพิ่มองค์ประกอบได้โดยไปที่กล่องเครื่องมือและเลือกองค์ประกอบที่คุณต้องการเพิ่ม เปลี่ยนชื่อปุ่มและเพิ่ม textView ใต้ปุ่ม การแสดง XML ของ AXML ของคุณควรมีลักษณะดังนี้:

 <?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 อ้างอิงถึงไฟล์ทรัพยากรสตริง ดังนั้นให้เปิดทรัพยากร/ค่า/strings.xml และทำการเปลี่ยนแปลงต่อไปนี้:

 <?xml version="1.0" encoding="utf-8"?> <resources> <string name="fivePM">It\'s 5PM Somewhere!</string> <string name="app_name">5PM Finder</string> </resources>

ตอนนี้เราได้สร้าง AXML ส่วนหน้าแล้ว ตอนนี้ขอเชื่อมต่อกับรหัสบางส่วน ไปที่ MainActivity.fs และทำการเปลี่ยนแปลงต่อไปนี้กับฟังก์ชัน onCreate ของคุณ:

 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/") )

แทนที่ fivepm.azurewebsites.net ด้วย URL ของการปรับใช้ JSON API ของคุณเอง เรียกใช้แอปพลิเคชันและคลิกที่ปุ่มในโปรแกรมจำลอง ในอีกสักครู่ คุณควรเห็น JSON API ส่งคืนพร้อมผลลัพธ์ API ของคุณ

การแยกวิเคราะห์ JSON

เราเกือบจะอยู่ที่นั่นแล้ว! ขณะนี้ แอปของเรากำลังแสดง JSON ดิบ และค่อนข้างอ่านไม่ได้ ขั้นตอนต่อไปคือการแยกวิเคราะห์ JSON และแสดงสตริงที่มนุษย์อ่านได้ชัดเจนยิ่งขึ้น ในการแยกวิเคราะห์ JSON เราสามารถใช้ไลบรารี Newtonsoft.JSON จากเซิร์ฟเวอร์ได้

ไปที่ตัวจัดการแพ็คเกจ NuGet ของคุณและค้นหา Newtonsoft.JSON ติดตั้งและกลับไปที่ไฟล์ MainActivity.fs นำเข้าโดยเพิ่ม "open Newtonsoft.Json"

ตอนนี้เพิ่มประเภท TZInfo ในโครงการ เราสามารถนำ TZInfo กลับมาใช้ใหม่จากเซิร์ฟเวอร์ได้ แต่เนื่องจากเราต้องการเพียงสองฟิลด์เท่านั้น เราจึงสามารถกำหนดประเภทที่กำหนดเองได้ที่นี่:

 type TZInfo = { tzName:string localTime: string }

เพิ่มคำจำกัดความประเภทเหนือฟังก์ชันหลัก และตอนนี้เปลี่ยนฟังก์ชันหลักดังนี้:

 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 )

ตอนนี้ผลลัพธ์ JSON API ถูกดีซีเรียลไลซ์ลงในวัตถุ TZInfo และใช้เพื่อสร้างสตริง เรียกใช้แอพและคลิกปุ่ม คุณควรเห็นสตริงที่จัดรูปแบบปรากฏขึ้นบนหน้าจอ

แม้ว่าแอปพลิเคชันนี้จะเรียบง่ายและอาจไม่ประณีต แต่เราได้สร้างแอปพลิเคชันมือถือที่ใช้และ F# JSON API แปลงข้อมูลและแสดงให้ผู้ใช้เห็น และเราทำทุกอย่างใน F# รู้สึกอิสระที่จะลองใช้ส่วนควบคุมต่างๆ และดูว่าคุณสามารถปรับปรุงการออกแบบได้หรือไม่

และที่นั่นเรามีมัน! แอปพลิเคชั่นมือถือ F# อย่างง่าย และ F# JSON API และบอกผู้ใช้ว่าตอนนี้ห้าโมงเย็นอยู่ที่ไหน

ห่อ

วันนี้เราเดินผ่านการสร้าง Web API แบบง่ายๆ และแอปพลิเคชัน Android แบบง่ายๆ โดยใช้ F# เท่านั้น ซึ่งแสดงให้เห็นทั้งความชัดเจนของภาษา F# และจุดแข็งของระบบนิเวศ F# อย่างไรก็ตาม เราแทบไม่ได้ขีดข่วนพื้นผิวของการพัฒนา F# ดังนั้นฉันจะเขียนบทความเพิ่มเติมสองสามโพสต์เพื่อต่อยอดจากสิ่งที่เราได้พูดคุยกันในวันนี้ นอกจากนี้ ฉันหวังว่าโพสต์นี้เป็นแรงบันดาลใจให้คุณสร้างแอปพลิเคชัน F# ของคุณเอง!

คุณสามารถหาโค้ดที่เราใช้สำหรับบทช่วยสอนนี้ได้ที่ GitHub