Noob용 Vinyl을 사용하여 Haskell에서 확장 가능한 레코드

16893 단어 haskellrecordvinyl
이 튜토리얼에서는 vinyl를 사용하여 하스켈 내에서 확장 가능한 레코드를 생성하는 방법을 보여 드리겠습니다.

이 튜토리얼은 이미 Haskell에 익숙하지만 사용법을 모르는 사람들을 대상으로 합니다vinyl.

우리는 비교로 javascript 객체/typescript 인터페이스를 사용할 것입니다.

메모


  • 스택 lts-17.1을 사용하고 있습니다.

  • 프로젝트 설정


  • stack new learn-vinyl를 사용하여 새 프로젝트를 만듭니다.
  • 이 라이브러리를 package.yaml에 추가하십시오: vinyl , microlens
  • Lib.hs에서 이 언어 확장을 추가하십시오.

  • {-# LANGUAGE DataKinds #-}
    {-# LANGUAGE FlexibleContexts #-}
    {-# LANGUAGE GADTs #-}
    {-# LANGUAGE NoMonomorphismRestriction #-}
    {-# LANGUAGE OverloadedLabels #-}
    {-# LANGUAGE OverloadedStrings #-}
    {-# LANGUAGE ScopedTypeVariables #-}
    {-# LANGUAGE TypeOperators #-} 
    


  • 다음 가져오기를 추가합니다.

  • import Data.Vinyl
    import Data.Vinyl.Syntax ()
    import Lens.Micro
    import Data.Text
    


    레코드 정의



    레코드Person를 정의해 보겠습니다.

    type Person = FieldRec
      '[ "name" ::: Text
       , "age" ::: Int
       , "is_single" ::: Bool
       ]
    


    이것은 다음 typescript 버전과 동일합니다.

    type Person = {
      name: string,
      age: number,
      is_single: boolean
    }
    


    기록 구성


    Person 유형의 레코드를 구성하려면 다음을 수행합니다.

    marcus :: Person
    marcus =
          #name =:= "Marcus"
      <+> #age =:= 58
      <+> #is_single =:= False
    


    타이프스크립트 버전:

    let marcus : Person = 
      {
        name: "Marcus", 
        age: 58, 
        is_single: false
      }
    


    경고: vinyl 레코드 필드는 문제에 순서를 적용하므로 잘못된 순서를 입력하면 유형 오류가 발생합니다.

    예시:

    bad_record :: Person
    bad_record =
          #age =:= 58
      <+> #name =:= "Marcus"
      <+> #is_single =:= False
    


    이것은 좋지 않습니다. 운 좋게도 rcast를 사용하여 필드를 재정렬하는 기능이 있습니다.

    marcus2 :: Person
    marcus2 = rcast $
          #age =:= (58 :: Int)
      <+> #name =:= ("Marcus" :: Text)
      <+> #is_single =:= False
    
    


    한 가지 나쁜 점은 일부 필드의 유형을 명시적으로 추가해야 한다는 것입니다.

    필드 액세스



    레코드 필드에 액세스하려면 마이크로렌즈에서 (^.) 연산자를 사용할 수 있습니다.

    run :: IO ()
    run = do
      print (marcus ^. #name)
    


    TS 버전:

    console.log(marcus.name)
    


    레코드 필드 설정




    run :: IO ()
    run = do
      let updated_marcus = marcus & #name .~ "Aurelius"
      print (updated_marcus ^. #name)
    


    타이프스크립트 버전:

    marcus.name = "Aurelius"
    console.log(marcus)
    


    중첩 레코드



    건설



    중첩 레코드를 구성하려면 다음을 추가합니다.

    type Empire = FieldRec
      '[ "king" ::: Person
       , "country" ::: Text
       ]
    
    rome :: Empire
    rome = 
          #king =:= marcus
      <+> #country =:= "Italy"
    


    중첩 필드 액세스




    run :: IO ()
    run = do
      print (rome ^. #king . #name)
    


    타이프스크립트 버전:

    console.log(rome.king.name)
    


    중첩 필드 설정




    run :: IO ()
    run = do
      let updated_rome = rome & #king . #name .~ "Aurelius"
      print (updated_rome ^. #king . #name)
    



    rome.king.name = "Aurelius"
    console.log(rome)
    


    결론


    vinyl는 매우 강력한 라이브러리이지만 간단한 자습서가 지연됩니다. 이것이 제가 이 튜토리얼을 만드는 이유입니다.

    앞으로는 레코드 하위 집합, 레코드 결합 등에 대해 이야기할 것입니다.

    전체 소스 코드:

    {-# LANGUAGE DataKinds #-}
    {-# LANGUAGE FlexibleContexts #-}
    {-# LANGUAGE GADTs #-}
    {-# LANGUAGE NoMonomorphismRestriction #-}
    {-# LANGUAGE OverloadedLabels #-}
    {-# LANGUAGE OverloadedStrings #-}
    {-# LANGUAGE ScopedTypeVariables #-}
    {-# LANGUAGE TypeOperators #-}
    
    module Lib
        ( run
        ) where
    
    import Data.Vinyl
    import Data.Vinyl.Syntax ()
    import Lens.Micro
    import Data.Text
    
    type Person = FieldRec
      '[ "name" ::: Text
       , "age" ::: Int
       , "is_single" ::: Bool
       ]
    
    type Empire = FieldRec
      '[ "king" ::: Person
       , "country" ::: Text
       ]
    
    marcus :: Person
    marcus =
          #name =:= "Marcus"
      <+> #age =:= 58
      <+> #is_single =:= False
    
    -- bad_record :: Person
    -- bad_record =
    --       #age =:= 58
    --   <+> #name =:= "Marcus"
    --   <+> #is_single =:= False
    
    marcus2 :: Person
    marcus2 = rcast $
          #age =:= (58 :: Int)
      <+> #name =:= ("Marcus" :: Text)
      <+> #is_single =:= False
    
    rome :: Empire
    rome =
          #king =:= marcus
      <+> #country =:= "Italy"
    
    
    run :: IO ()
    run = do
      print (marcus ^. #name)
    
      let updated_marcus = marcus & #name .~ "Aurelius"
      print (updated_marcus ^. #name)
    
      -- Nested
      print (rome ^. #king . #name)
    
      let updated_rome = rome & #king . #name .~ "Aurelius"
      print (updated_rome ^. #king . #name)
    
    

    좋은 웹페이지 즐겨찾기