// Copyright (c) 2023 IBM Corp. // All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Prism is an optic used to select part of a sum type. package prism import ( EM "github.com/IBM/fp-go/endomorphism" F "github.com/IBM/fp-go/function" O "github.com/IBM/fp-go/option" ) type ( // Prism is an optic used to select part of a sum type. Prism[S, A any] interface { GetOption(s S) O.Option[A] ReverseGet(a A) S } prismImpl[S, A any] struct { get func(S) O.Option[A] rev func(A) S } ) func (prism prismImpl[S, A]) GetOption(s S) O.Option[A] { return prism.get(s) } func (prism prismImpl[S, A]) ReverseGet(a A) S { return prism.rev(a) } func MakePrism[S, A any](get func(S) O.Option[A], rev func(A) S) Prism[S, A] { return prismImpl[S, A]{get, rev} } // Id returns a prism implementing the identity operation func Id[S any]() Prism[S, S] { return MakePrism(O.Some[S], F.Identity[S]) } func FromPredicate[S any](pred func(S) bool) Prism[S, S] { return MakePrism(O.FromPredicate(pred), F.Identity[S]) } // Compose composes a `Prism` with a `Prism`. func Compose[S, A, B any](ab Prism[A, B]) func(Prism[S, A]) Prism[S, B] { return func(sa Prism[S, A]) Prism[S, B] { return MakePrism(F.Flow2( sa.GetOption, O.Chain(ab.GetOption), ), F.Flow2( ab.ReverseGet, sa.ReverseGet, )) } } func prismModifyOption[S, A any](f func(A) A, sa Prism[S, A], s S) O.Option[S] { return F.Pipe2( s, sa.GetOption, O.Map(F.Flow2( f, sa.ReverseGet, )), ) } func prismModify[S, A any](f func(A) A, sa Prism[S, A], s S) S { return F.Pipe1( prismModifyOption(f, sa, s), O.GetOrElse(F.Constant(s)), ) } func prismSet[S, A any](a A) func(Prism[S, A]) EM.Endomorphism[S] { return EM.Curry3(prismModify[S, A])(F.Constant1[A](a)) } func Set[S, A any](a A) func(Prism[S, A]) EM.Endomorphism[S] { return EM.Curry3(prismModify[S, A])(F.Constant1[A](a)) } func prismSome[A any]() Prism[O.Option[A], A] { return MakePrism(F.Identity[O.Option[A]], O.Some[A]) } // Some returns a `Prism` from a `Prism` focused on the `Some` of a `Option` type. func Some[S, A any](soa Prism[S, O.Option[A]]) Prism[S, A] { return Compose[S](prismSome[A]())(soa) } func imap[S any, AB ~func(A) B, BA ~func(B) A, A, B any](sa Prism[S, A], ab AB, ba BA) Prism[S, B] { return MakePrism( F.Flow2(sa.GetOption, O.Map(ab)), F.Flow2(ba, sa.ReverseGet), ) } func IMap[S any, AB ~func(A) B, BA ~func(B) A, A, B any](ab AB, ba BA) func(Prism[S, A]) Prism[S, B] { return func(sa Prism[S, A]) Prism[S, B] { return imap(sa, ab, ba) } }