edit
this is getting hairy, and my answer didn't accounted precisely for request... so let's see your code with minimal change:
concat([], L, L).
concat([H|T], L, [H|Res]) :-
concat(T, L, Res).
repl([], _, _, []).
repl([Val|T], Val, Repl, Res) :- !, % as noted by @repeat, better to commit early...
repl(T, Val, Repl, Temp),
concat(Repl, Temp, Res). % !.
repl([H|T], Val, Repl, [H|Res]) :-
repl(T, Val, Repl, Res).
the cut simply commits the second clause...
resume old answer
your concat/3 is the same as the well known append/3, so consider this approach
repl(ListOrig, Element, Replace, ListUpdated) :-
append(H, [Element|T], ListOrig),
append(H, Replace, Temp),
append(Temp, T, ListUpdated).
?- repl([1, 2, 3], 3, [2, 3, 4], L).
L = [1, 2, 2, 3, 4] ;
false.
edit
as requested by comments, this extension handles a list of Element to match for change, with simple pattern matching (note: add before the previous clause)
repl(ListOrig, [], _Replace, ListOrig).
repl(ListOrig, [E|Es], Replace, ListUpdated) :-
repl(ListOrig, E, Replace, Temp),
repl(Temp, Es, Replace, ListUpdated).
test
?- repl([1,2,3],[2,3],[x,y,z],R).
R = [1, x, y, z, x, y, z] ;
false.
edit
I didn't noticed that if Element is not found it should not fail...
a last 'catchall' clause could handle this case:
repl(ListOrig, _Element, _Replace, ListOrig).
or better, extend the original like
repl(ListOrig, Element, Replace, ListUpdated) :-
( append(H, [Element|T], ListOrig)
-> append(H, Replace, Temp),
append(Temp, T, ListUpdated)
; ListOrig = ListUpdated
).