mirror of
https://github.com/IBM/fp-go.git
synced 2025-11-25 22:21:49 +02:00
fix: some internal refactorings
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
This commit is contained in:
@@ -58,7 +58,7 @@ func MapWithIndex[A, B any](f func(int, A) B) func([]A) []B {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Map[A, B any](f func(a A) B) func([]A) []B {
|
func Map[A, B any](f func(a A) B) func([]A) []B {
|
||||||
return F.Bind2nd(MonadMap[A, B], f)
|
return G.Map[[]A, []B, A, B](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
func MapRef[A, B any](f func(a *A) B) func([]A) []B {
|
func MapRef[A, B any](f func(a *A) B) func([]A) []B {
|
||||||
|
|||||||
@@ -147,7 +147,7 @@ func MonadMap[GA ~[]A, GB ~[]B, A, B any](as GA, f func(a A) B) GB {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Map[GA ~[]A, GB ~[]B, A, B any](f func(a A) B) func(GA) GB {
|
func Map[GA ~[]A, GB ~[]B, A, B any](f func(a A) B) func(GA) GB {
|
||||||
return F.Bind2nd(MonadMap[GA, GB, A, B], f)
|
return array.Map[GA, GB](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
func MonadMapWithIndex[GA ~[]A, GB ~[]B, A, B any](as GA, f func(int, A) B) GB {
|
func MonadMapWithIndex[GA ~[]A, GB ~[]B, A, B any](as GA, f func(int, A) B) GB {
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ package either
|
|||||||
import (
|
import (
|
||||||
E "github.com/IBM/fp-go/errors"
|
E "github.com/IBM/fp-go/errors"
|
||||||
F "github.com/IBM/fp-go/function"
|
F "github.com/IBM/fp-go/function"
|
||||||
|
C "github.com/IBM/fp-go/internal/chain"
|
||||||
FC "github.com/IBM/fp-go/internal/functor"
|
FC "github.com/IBM/fp-go/internal/functor"
|
||||||
L "github.com/IBM/fp-go/lazy"
|
L "github.com/IBM/fp-go/lazy"
|
||||||
O "github.com/IBM/fp-go/option"
|
O "github.com/IBM/fp-go/option"
|
||||||
@@ -85,9 +86,12 @@ func MonadChain[E, A, B any](fa Either[E, A], f func(a A) Either[E, B]) Either[E
|
|||||||
}
|
}
|
||||||
|
|
||||||
func MonadChainFirst[E, A, B any](ma Either[E, A], f func(a A) Either[E, B]) Either[E, A] {
|
func MonadChainFirst[E, A, B any](ma Either[E, A], f func(a A) Either[E, B]) Either[E, A] {
|
||||||
return MonadChain(ma, func(a A) Either[E, A] {
|
return C.MonadChainFirst(
|
||||||
return MonadMap(f(a), F.Constant1[B](a))
|
MonadChain[E, A, A],
|
||||||
})
|
MonadMap[E, B, A],
|
||||||
|
ma,
|
||||||
|
f,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func MonadChainTo[A, E, B any](ma Either[E, A], mb Either[E, B]) Either[E, B] {
|
func MonadChainTo[A, E, B any](ma Either[E, A], mb Either[E, B]) Either[E, B] {
|
||||||
@@ -114,7 +118,11 @@ func Chain[E, A, B any](f func(a A) Either[E, B]) func(Either[E, A]) Either[E, B
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ChainFirst[E, A, B any](f func(a A) Either[E, B]) func(Either[E, A]) Either[E, A] {
|
func ChainFirst[E, A, B any](f func(a A) Either[E, B]) func(Either[E, A]) Either[E, A] {
|
||||||
return F.Bind2nd(MonadChainFirst[E, A, B], f)
|
return C.ChainFirst(
|
||||||
|
Chain[E, A, A],
|
||||||
|
Map[E, B, A],
|
||||||
|
f,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Flatten[E, A any](mma Either[E, Either[E, A]]) Either[E, A] {
|
func Flatten[E, A any](mma Either[E, Either[E, A]]) Either[E, A] {
|
||||||
|
|||||||
@@ -19,38 +19,17 @@ import (
|
|||||||
F "github.com/IBM/fp-go/function"
|
F "github.com/IBM/fp-go/function"
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
|
||||||
*
|
|
||||||
We need to pass the members of the applicative explicitly, because golang does neither support higher kinded types nor template methods on structs or interfaces
|
|
||||||
|
|
||||||
HKTRB = HKT<Either[B]>
|
|
||||||
HKTA = HKT<A>
|
|
||||||
HKTB = HKT<B>
|
|
||||||
*/
|
|
||||||
func traverse[E, A, B, HKTB, HKTRB any](
|
|
||||||
mof func(Either[E, B]) HKTRB,
|
|
||||||
mmap func(func(B) Either[E, B]) func(HKTB) HKTRB,
|
|
||||||
) func(Either[E, A], func(A) HKTB) HKTRB {
|
|
||||||
|
|
||||||
left := F.Flow2(Left[B, E], mof)
|
|
||||||
right := mmap(Right[E, B])
|
|
||||||
|
|
||||||
return func(ta Either[E, A], f func(A) HKTB) HKTRB {
|
|
||||||
return MonadFold(ta,
|
|
||||||
left,
|
|
||||||
F.Flow2(f, right),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Traverse converts an [Either] of some higher kinded type into the higher kinded type of an [Either]
|
// Traverse converts an [Either] of some higher kinded type into the higher kinded type of an [Either]
|
||||||
func Traverse[A, E, B, HKTB, HKTRB any](
|
func Traverse[A, E, B, HKTB, HKTRB any](
|
||||||
mof func(Either[E, B]) HKTRB,
|
mof func(Either[E, B]) HKTRB,
|
||||||
mmap func(func(B) Either[E, B]) func(HKTB) HKTRB,
|
mmap func(func(B) Either[E, B]) func(HKTB) HKTRB,
|
||||||
) func(func(A) HKTB) func(Either[E, A]) HKTRB {
|
) func(func(A) HKTB) func(Either[E, A]) HKTRB {
|
||||||
delegate := traverse[E, A, B](mof, mmap)
|
|
||||||
|
left := F.Flow2(Left[B, E], mof)
|
||||||
|
right := mmap(Right[E, B])
|
||||||
|
|
||||||
return func(f func(A) HKTB) func(Either[E, A]) HKTRB {
|
return func(f func(A) HKTB) func(Either[E, A]) HKTRB {
|
||||||
return F.Bind2nd(delegate, f)
|
return Fold(left, F.Flow2(f, right))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -106,6 +106,12 @@ func MonadMap[GA ~[]A, GB ~[]B, A, B any](as GA, f func(a A) B) GB {
|
|||||||
return bs
|
return bs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Map[GA ~[]A, GB ~[]B, A, B any](f func(a A) B) func(GA) GB {
|
||||||
|
return func(as GA) GB {
|
||||||
|
return MonadMap[GA, GB](as, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func MonadMapWithIndex[GA ~[]A, GB ~[]B, A, B any](as GA, f func(idx int, a A) B) GB {
|
func MonadMapWithIndex[GA ~[]A, GB ~[]B, A, B any](as GA, f func(idx int, a A) B) GB {
|
||||||
count := len(as)
|
count := len(as)
|
||||||
bs := make(GB, count)
|
bs := make(GB, count)
|
||||||
|
|||||||
@@ -159,3 +159,7 @@ func OrLeft[E1, E2, A, HKTE1A, HKTE2, HKTE2A any](
|
|||||||
func MonadMapLeft[E, A, B, HKTFA, HKTFB any](fmap func(HKTFA, func(ET.Either[E, A]) ET.Either[B, A]) HKTFB, fa HKTFA, f func(E) B) HKTFB {
|
func MonadMapLeft[E, A, B, HKTFA, HKTFB any](fmap func(HKTFA, func(ET.Either[E, A]) ET.Either[B, A]) HKTFB, fa HKTFA, f func(E) B) HKTFB {
|
||||||
return FC.MonadMap(fmap, ET.MonadMapLeft[E, A, B], fa, f)
|
return FC.MonadMap(fmap, ET.MonadMapLeft[E, A, B], fa, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func MapLeft[E, A, B, HKTFA, HKTFB any](fmap func(func(ET.Either[E, A]) ET.Either[B, A]) func(HKTFA) HKTFB, f func(E) B) func(HKTFA) HKTFB {
|
||||||
|
return FC.Map(fmap, ET.MapLeft[A, E, B], f)
|
||||||
|
}
|
||||||
|
|||||||
@@ -40,6 +40,12 @@ func MonadMap[A, B, HKTFA, HKTFB any](fmap func(HKTFA, func(O.Option[A]) O.Optio
|
|||||||
return FC.MonadMap(fmap, O.MonadMap[A, B], fa, f)
|
return FC.MonadMap(fmap, O.MonadMap[A, B], fa, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Map[A, B, HKTFA, HKTFB any](fmap func(func(O.Option[A]) O.Option[B]) func(HKTFA) HKTFB, f func(A) B) func(HKTFA) HKTFB {
|
||||||
|
// HKTGA = Either[E, A]
|
||||||
|
// HKTGB = Either[E, B]
|
||||||
|
return FC.Map(fmap, O.Map[A, B], f)
|
||||||
|
}
|
||||||
|
|
||||||
func MonadChain[A, B, HKTFA, HKTFB any](
|
func MonadChain[A, B, HKTFA, HKTFB any](
|
||||||
fchain func(HKTFA, func(O.Option[A]) HKTFB) HKTFB,
|
fchain func(HKTFA, func(O.Option[A]) HKTFB) HKTFB,
|
||||||
fof func(O.Option[B]) HKTFB,
|
fof func(O.Option[B]) HKTFB,
|
||||||
|
|||||||
@@ -156,7 +156,10 @@ func MonadAp[GB ~func() ET.Either[E, B], GAB ~func() ET.Either[E, func(A) B], GA
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Ap[GB ~func() ET.Either[E, B], GAB ~func() ET.Either[E, func(A) B], GA ~func() ET.Either[E, A], E, A, B any](ma GA) func(GAB) GB {
|
func Ap[GB ~func() ET.Either[E, B], GAB ~func() ET.Either[E, func(A) B], GA ~func() ET.Either[E, A], E, A, B any](ma GA) func(GAB) GB {
|
||||||
return F.Bind2nd(MonadAp[GB, GAB, GA], ma)
|
return eithert.Ap(
|
||||||
|
IO.Ap[GB, func() func(ET.Either[E, A]) ET.Either[E, B], GA, ET.Either[E, B], ET.Either[E, A]],
|
||||||
|
IO.Map[GAB, func() func(ET.Either[E, A]) ET.Either[E, B], ET.Either[E, func(A) B], func(ET.Either[E, A]) ET.Either[E, B]],
|
||||||
|
ma)
|
||||||
}
|
}
|
||||||
|
|
||||||
func MonadApSeq[GB ~func() ET.Either[E, B], GAB ~func() ET.Either[E, func(A) B], GA ~func() ET.Either[E, A], E, A, B any](mab GAB, ma GA) GB {
|
func MonadApSeq[GB ~func() ET.Either[E, B], GAB ~func() ET.Either[E, func(A) B], GA ~func() ET.Either[E, A], E, A, B any](mab GAB, ma GA) GB {
|
||||||
@@ -167,7 +170,10 @@ func MonadApSeq[GB ~func() ET.Either[E, B], GAB ~func() ET.Either[E, func(A) B],
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ApSeq[GB ~func() ET.Either[E, B], GAB ~func() ET.Either[E, func(A) B], GA ~func() ET.Either[E, A], E, A, B any](ma GA) func(GAB) GB {
|
func ApSeq[GB ~func() ET.Either[E, B], GAB ~func() ET.Either[E, func(A) B], GA ~func() ET.Either[E, A], E, A, B any](ma GA) func(GAB) GB {
|
||||||
return F.Bind2nd(MonadApSeq[GB, GAB, GA], ma)
|
return eithert.Ap(
|
||||||
|
IO.ApSeq[GB, func() func(ET.Either[E, A]) ET.Either[E, B], GA, ET.Either[E, B], ET.Either[E, A]],
|
||||||
|
IO.Map[GAB, func() func(ET.Either[E, A]) ET.Either[E, B], ET.Either[E, func(A) B], func(ET.Either[E, A]) ET.Either[E, B]],
|
||||||
|
ma)
|
||||||
}
|
}
|
||||||
|
|
||||||
func MonadApPar[GB ~func() ET.Either[E, B], GAB ~func() ET.Either[E, func(A) B], GA ~func() ET.Either[E, A], E, A, B any](mab GAB, ma GA) GB {
|
func MonadApPar[GB ~func() ET.Either[E, B], GAB ~func() ET.Either[E, func(A) B], GA ~func() ET.Either[E, A], E, A, B any](mab GAB, ma GA) GB {
|
||||||
@@ -178,7 +184,10 @@ func MonadApPar[GB ~func() ET.Either[E, B], GAB ~func() ET.Either[E, func(A) B],
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ApPar[GB ~func() ET.Either[E, B], GAB ~func() ET.Either[E, func(A) B], GA ~func() ET.Either[E, A], E, A, B any](ma GA) func(GAB) GB {
|
func ApPar[GB ~func() ET.Either[E, B], GAB ~func() ET.Either[E, func(A) B], GA ~func() ET.Either[E, A], E, A, B any](ma GA) func(GAB) GB {
|
||||||
return F.Bind2nd(MonadApPar[GB, GAB, GA], ma)
|
return eithert.Ap(
|
||||||
|
IO.ApPar[GB, func() func(ET.Either[E, A]) ET.Either[E, B], GA, ET.Either[E, B], ET.Either[E, A]],
|
||||||
|
IO.Map[GAB, func() func(ET.Either[E, A]) ET.Either[E, B], ET.Either[E, func(A) B], func(ET.Either[E, A]) ET.Either[E, B]],
|
||||||
|
ma)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Flatten[GA ~func() ET.Either[E, A], GAA ~func() ET.Either[E, GA], E, A any](mma GAA) GA {
|
func Flatten[GA ~func() ET.Either[E, A], GAA ~func() ET.Either[E, GA], E, A any](mma GAA) GA {
|
||||||
@@ -204,11 +213,18 @@ func Memoize[GA ~func() ET.Either[E, A], E, A any](ma GA) GA {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func MonadMapLeft[GA1 ~func() ET.Either[E1, A], GA2 ~func() ET.Either[E2, A], E1, E2, A any](fa GA1, f func(E1) E2) GA2 {
|
func MonadMapLeft[GA1 ~func() ET.Either[E1, A], GA2 ~func() ET.Either[E2, A], E1, E2, A any](fa GA1, f func(E1) E2) GA2 {
|
||||||
return eithert.MonadMapLeft(IO.MonadMap[GA1, GA2, ET.Either[E1, A], ET.Either[E2, A]], fa, f)
|
return eithert.MonadMapLeft(
|
||||||
|
IO.MonadMap[GA1, GA2, ET.Either[E1, A], ET.Either[E2, A]],
|
||||||
|
fa,
|
||||||
|
f,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func MapLeft[GA1 ~func() ET.Either[E1, A], GA2 ~func() ET.Either[E2, A], E1, E2, A any](f func(E1) E2) func(GA1) GA2 {
|
func MapLeft[GA1 ~func() ET.Either[E1, A], GA2 ~func() ET.Either[E2, A], E1, E2, A any](f func(E1) E2) func(GA1) GA2 {
|
||||||
return F.Bind2nd(MonadMapLeft[GA1, GA2, E1, E2, A], f)
|
return eithert.MapLeft(
|
||||||
|
IO.Map[GA1, GA2, ET.Either[E1, A], ET.Either[E2, A]],
|
||||||
|
f,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delay creates an operation that passes in the value after some [time.Duration]
|
// Delay creates an operation that passes in the value after some [time.Duration]
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ func MonadMap[GA ~func() O.Option[A], GB ~func() O.Option[B], A, B any](fa GA, f
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Map[GA ~func() O.Option[A], GB ~func() O.Option[B], A, B any](f func(A) B) func(GA) GB {
|
func Map[GA ~func() O.Option[A], GB ~func() O.Option[B], A, B any](f func(A) B) func(GA) GB {
|
||||||
return F.Bind2nd(MonadMap[GA, GB, A, B], f)
|
return optiont.Map(IO.Map[GA, GB, O.Option[A], O.Option[B]], f)
|
||||||
}
|
}
|
||||||
|
|
||||||
func MonadChain[GA ~func() O.Option[A], GB ~func() O.Option[B], A, B any](fa GA, f func(A) GB) GB {
|
func MonadChain[GA ~func() O.Option[A], GB ~func() O.Option[B], A, B any](fa GA, f func(A) GB) GB {
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ package generic
|
|||||||
import (
|
import (
|
||||||
A "github.com/IBM/fp-go/array/generic"
|
A "github.com/IBM/fp-go/array/generic"
|
||||||
F "github.com/IBM/fp-go/function"
|
F "github.com/IBM/fp-go/function"
|
||||||
|
C "github.com/IBM/fp-go/internal/chain"
|
||||||
"github.com/IBM/fp-go/internal/utils"
|
"github.com/IBM/fp-go/internal/utils"
|
||||||
IO "github.com/IBM/fp-go/iooption/generic"
|
IO "github.com/IBM/fp-go/iooption/generic"
|
||||||
M "github.com/IBM/fp-go/monoid"
|
M "github.com/IBM/fp-go/monoid"
|
||||||
@@ -146,6 +147,23 @@ func MonadChain[GV ~func() O.Option[T.Tuple2[GV, V]], GU ~func() O.Option[T.Tupl
|
|||||||
return Chain[GV, GU](f)(ma)
|
return Chain[GV, GU](f)(ma)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func MonadChainFirst[GV ~func() O.Option[T.Tuple2[GV, V]], GU ~func() O.Option[T.Tuple2[GU, U]], U, V any](ma GU, f func(U) GV) GU {
|
||||||
|
return C.MonadChainFirst(
|
||||||
|
MonadChain[GU, GU, U, U],
|
||||||
|
MonadMap[GU, GV, V, U],
|
||||||
|
ma,
|
||||||
|
f,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ChainFirst[GV ~func() O.Option[T.Tuple2[GV, V]], GU ~func() O.Option[T.Tuple2[GU, U]], U, V any](f func(U) GV) func(GU) GU {
|
||||||
|
return C.ChainFirst(
|
||||||
|
Chain[GU, GU, U, U],
|
||||||
|
Map[GU, GV, func(V) U, V, U],
|
||||||
|
f,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
func Flatten[GV ~func() O.Option[T.Tuple2[GV, GU]], GU ~func() O.Option[T.Tuple2[GU, U]], U any](ma GV) GU {
|
func Flatten[GV ~func() O.Option[T.Tuple2[GV, GU]], GU ~func() O.Option[T.Tuple2[GU, U]], U any](ma GV) GU {
|
||||||
return MonadChain(ma, F.Identity[GU])
|
return MonadChain(ma, F.Identity[GU])
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -144,3 +144,11 @@ func FoldMap[U, V any](m M.Monoid[V]) func(func(U) V) func(ma Iterator[U]) V {
|
|||||||
func Fold[U any](m M.Monoid[U]) func(Iterator[U]) U {
|
func Fold[U any](m M.Monoid[U]) func(Iterator[U]) U {
|
||||||
return G.Fold[Iterator[U]](m)
|
return G.Fold[Iterator[U]](m)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func MonadChainFirst[U, V any](ma Iterator[U], f func(U) Iterator[V]) Iterator[U] {
|
||||||
|
return G.MonadChainFirst[Iterator[V], Iterator[U], U, V](ma, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ChainFirst[U, V any](f func(U) Iterator[V]) func(Iterator[U]) Iterator[U] {
|
||||||
|
return G.ChainFirst[Iterator[V], Iterator[U], U, V](f)
|
||||||
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ package option
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
F "github.com/IBM/fp-go/function"
|
F "github.com/IBM/fp-go/function"
|
||||||
|
C "github.com/IBM/fp-go/internal/chain"
|
||||||
FC "github.com/IBM/fp-go/internal/functor"
|
FC "github.com/IBM/fp-go/internal/functor"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -95,7 +96,7 @@ func MonadChain[A, B any](fa Option[A], f func(A) Option[B]) Option[B] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Chain[A, B any](f func(A) Option[B]) func(Option[A]) Option[B] {
|
func Chain[A, B any](f func(A) Option[B]) func(Option[A]) Option[B] {
|
||||||
return F.Bind2nd(MonadChain[A, B], f)
|
return Fold(None[B], f)
|
||||||
}
|
}
|
||||||
|
|
||||||
func MonadChainTo[A, B any](ma Option[A], mb Option[B]) Option[B] {
|
func MonadChainTo[A, B any](ma Option[A], mb Option[B]) Option[B] {
|
||||||
@@ -107,13 +108,20 @@ func ChainTo[A, B any](mb Option[B]) func(Option[A]) Option[B] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func MonadChainFirst[A, B any](ma Option[A], f func(A) Option[B]) Option[A] {
|
func MonadChainFirst[A, B any](ma Option[A], f func(A) Option[B]) Option[A] {
|
||||||
return MonadChain(ma, func(a A) Option[A] {
|
return C.MonadChainFirst(
|
||||||
return MonadMap(f(a), F.Constant1[B](a))
|
MonadChain[A, A],
|
||||||
})
|
MonadMap[B, A],
|
||||||
|
ma,
|
||||||
|
f,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ChainFirst[A, B any](f func(A) Option[B]) func(Option[A]) Option[A] {
|
func ChainFirst[A, B any](f func(A) Option[B]) func(Option[A]) Option[A] {
|
||||||
return F.Bind2nd(MonadChainFirst[A, B], f)
|
return C.ChainFirst(
|
||||||
|
Chain[A, A],
|
||||||
|
Map[B, A],
|
||||||
|
f,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Flatten[A any](mma Option[Option[A]]) Option[A] {
|
func Flatten[A any](mma Option[Option[A]]) Option[A] {
|
||||||
|
|||||||
Reference in New Issue
Block a user