Last active
December 18, 2024 18:39
-
-
Save sgf-dma/f97f73f209bb98a07db6e170d2e18854 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package main | |
| import ( | |
| "fmt" | |
| "encoding/json" | |
| "reflect" | |
| ) | |
| // Unmarshal json to a struct containing interface field. See full | |
| // corresponding Haskell code in separate file. | |
| // Haskell: | |
| // class I a where | |
| // f :: a -> () | |
| type I interface { | |
| f() | |
| } | |
| type S struct { | |
| X int | |
| } | |
| var _ I = S{} | |
| func (s S) f() {} | |
| // Haskell: data R1 = forall a. I a => R1 a Int | |
| // I.e. data constructor type is 'R1 :: forall a. I a => a -> Int -> R1', | |
| // and the type is existential. | |
| type R1 struct { | |
| I I | |
| Y int | |
| } | |
| // Haskell: data R2 a = R2 a Int | |
| // I.e. data constructor type is 'R2 :: forall a. a -> Int -> R2 a', | |
| // and this is regular (not existential) type. | |
| type R2[T any] struct { | |
| I T | |
| Y int | |
| } | |
| // Version of 'S' with pointer receiver for use with 'R3'. | |
| type SP struct { | |
| X int | |
| } | |
| var _ I = (*SP)(nil) | |
| func (s *SP) f() {} | |
| // Haskell does not have reflect, so there is no equivalent of 'R3'. | |
| type R3 struct { | |
| I I | |
| Y int | |
| } | |
| func (r *R3) UnmarshalJSON(bs []byte) (err error) { | |
| v := struct { | |
| I json.RawMessage | |
| Y int | |
| }{} | |
| err = json.Unmarshal(bs, &v) | |
| if err != nil { | |
| return | |
| } | |
| r.Y = v.Y | |
| if r.I != nil { | |
| p := reflect.ValueOf(r.I) | |
| if p.Kind() != reflect.Pointer { | |
| err = fmt.Errorf("R3.UnmarshalJSON(): Interface 'r.I' uses value receiver '%v'", p.Type()) | |
| return | |
| } | |
| if err = json.Unmarshal(v.I, p.Interface()); err != nil { | |
| return | |
| } | |
| } | |
| return | |
| } | |
| // Haskell: | |
| // class Read a => I2 a where | |
| // f2 :: a -> () | |
| type I2 interface { | |
| f2() | |
| json.Unmarshaler | |
| } | |
| type S2 struct { | |
| X int | |
| } | |
| // Dummy instance, but it's required by 'I2'. | |
| var _ json.Unmarshaler = (*S2)(nil) | |
| func (s *S2) UnmarshalJSON(bs []byte) error { | |
| v := struct {X int}{} | |
| if err := json.Unmarshal(bs, &v); err != nil { | |
| return err | |
| } | |
| s.X = v.X | |
| return nil | |
| } | |
| // Implementing I2 with value receiver has no sense, because | |
| // 'json.Unmarshaler' can only work with pointer receiver. | |
| var _ I2 = (*S2)(nil) | |
| func (s *S2) f2() {} | |
| // Haskell: data R4 = forall a. I2 a => R4 a Int | |
| // I.e. data constructor is: 'R4 :: forall a. I2 a => a -> Int -> R4' | |
| type R4 struct { | |
| I I2 | |
| Y int | |
| } | |
| // json.UnmarshalJSON() method in Go has a pointer to actual data, so it | |
| // actually does know what type is behind existential interface 'I2'. But | |
| // Haskell only uses types and does not look at values during type check. | |
| // Therefore, a function equivalent to 'json.UnmarshalJSON()' should have a | |
| // type witness, which tell Haskell, which type to use for interface 'I2'. So, | |
| // the implementation of 'json.Unmarshaler will look like: | |
| // | |
| // class Unmarshaler b a where | |
| // read' :: Proxy a -> String -> b | |
| // | |
| // and an instance for R4 will be: | |
| // | |
| // instance I2 a => Unmarshaler R4 a where | |
| func main() { | |
| jsBytes := []byte(`{"I": {"X": 1}, "Y": 2}`) | |
| var err error | |
| r1 := R1{} | |
| err = json.Unmarshal(jsBytes, &r1) | |
| fmt.Printf("R1: err = %v, r1 = %v\n", err, r1) | |
| r2 := R2[S]{} | |
| err = json.Unmarshal(jsBytes, &r2) | |
| fmt.Printf("R2: err = %v, r2 = %v\n", err, r2) | |
| r3 := R3{I: S{}} | |
| err = json.Unmarshal(jsBytes, &r3) | |
| fmt.Printf("R3{S}: err = %v, r3 = %v\n", err, r3) | |
| r3p := R3{I: &SP{}} | |
| err = json.Unmarshal(jsBytes, &r3p) | |
| fmt.Printf("R3{SP}: err = %v, r3p = %v, r3p.I = %v\n", err, r3p, r3p.I) | |
| r4 := R4{I: &S2{}} | |
| err = json.Unmarshal(jsBytes, &r4) | |
| fmt.Printf("R4: err = %v, r4 = %v, r4.I = %v\n", err, r4, r4.I) | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment